From c7bde1d40946702b74055efdf8913a1ba7e43c3f Mon Sep 17 00:00:00 2001 From: Brad Frost Date: Fri, 28 Mar 2014 23:51:22 -0400 Subject: [PATCH 001/166] Added "allow-popups allow-forms" to iframe sandbox Adding these to sandbox so that link variables can work with forms --- core/templates/index.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/index.mustache b/core/templates/index.mustache index 531210516..dfccf6a29 100644 --- a/core/templates/index.mustache +++ b/core/templates/index.mustache @@ -34,7 +34,7 @@
- +
From ea17b5b4bc9148d20bedc9fa4446cfdb3a984a10 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 18 Apr 2014 23:12:49 -0400 Subject: [PATCH 002/166] allowing inclusion of link.[pattern-name] in JSON --- core/lib/PatternLab/Builder.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index d4ae9dddf..762990a66 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -867,6 +867,9 @@ protected function gatherPatternInfo() { } } + // walk across the data and update links + array_walk_recursive($this->d,'PatternLab\Builder::compareReplace'); + // make sure $this->mpl is refreshed $this->loadMustachePatternLoaderInstance(); @@ -1341,6 +1344,23 @@ public function printData() { print_r($this->d); } + /** + * Go through data and replace any values that match items from the link.array + * @param {String} an entry from one of the list-based config entries + * + * @return {String} trimmed version of the given $v var + */ + public function compareReplace(&$value) { + if (is_string($value)) { + $valueCheck = strtolower($value); + $valueThin = str_replace("link.","",$valueCheck); + if ((strpos($valueCheck, 'link.') !== false) && array_key_exists($valueThin,$this->d["link"])) { + $value = $this->d["link"][$valueThin]; + } + } + + } + /** * Trim a given string. Used in the array_walk() function in __construct as a sanity check * @param {String} an entry from one of the list-based config entries From 4edf983da9844df7975f051cbd4dddd15c67bd38 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:37:48 -0400 Subject: [PATCH 003/166] moving the mustache pattern loader into its own class --- core/lib/PatternLab/PatternLoader.php | 221 ++++++++++++++++++ .../PatternLab/PatternLoaders/Mustache.php | 156 +++++++++++++ 2 files changed, 377 insertions(+) create mode 100644 core/lib/PatternLab/PatternLoader.php create mode 100644 core/lib/PatternLab/PatternLoaders/Mustache.php diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php new file mode 100644 index 000000000..8886d7ceb --- /dev/null +++ b/core/lib/PatternLab/PatternLoader.php @@ -0,0 +1,221 @@ +patternPaths = $patternPaths; + + } + + /** + * Helper function to return the pattern file name + * @param {String} the name of the pattern + * + * @return {String} the file path to the pattern + */ + public function getPatternFileName($name) { + + $patternFileName = ""; + + list($patternType,$pattern) = $this->getPatternInfo($name); + + // see if the pattern is an exact match for patternPaths. if not iterate over patternPaths to find a likely match + if (isset($this->patternPaths[$patternType][$pattern])) { + $patternFileName = $this->patternPaths[$patternType][$pattern]["patternSrcPath"]; + } else if (isset($this->patternPaths[$patternType])) { + foreach($this->patternPaths[$patternType] as $patternMatchKey=>$patternMatchValue) { + $pos = strpos($patternMatchKey,$pattern); + if ($pos !== false) { + $patternFileName = $patternMatchValue["patternSrcPath"]; + break; + } + } + } + + return $patternFileName; + + } + + /** + * Helper function to return the parts of a partial name + * @param {String} the name of the partial + * + * @return {Array} the pattern type and the name of the pattern + */ + public function getPatternInfo($name) { + + $patternBits = explode("-",$name); + + $i = 1; + $k = 2; + $c = count($patternBits); + $patternType = $patternBits[0]; + while (!isset($this->patternPaths[$patternType]) && ($i < $c)) { + $patternType .= "-".$patternBits[$i]; + $i++; + $k++; + } + + $patternBits = explode("-",$name,$k); + $pattern = $patternBits[count($patternBits)-1]; + + return array($patternType, $pattern); + + } + + /** + * Helper function for finding if a partial name has style modifier or parameters + * @param {String} the pattern name + * + * @return {Array} an array containing just the partial name, a style modifier, and any parameters + */ + public function getPartialInfo($partial) { + + $styleModifier = array(); + $parameters = array(); + + if (strpos($partial, "(") !== false) { + $partialBits = explode("(",$partial,2); + $partial = trim($partialBits[0]); + $parametersString = substr($partialBits[1],0,(strlen($partialBits[1]) - strlen(strrchr($partialBits[1],")")))); + $parameters = $this->parseParameters($parametersString); + } + + if (strpos($partial, ":") !== false) { + $partialBits = explode(":",$partial,2); + $partial = $partialBits[0]; + $styleModifier = array("styleModifier" => $partialBits[1]); + } + + return array($partial,$styleModifier,$parameters); + + } + + /** + * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache + * @param {String} the file contents + * @param {Array} an array of paramters to match + * + * @return {String} the modified file contents + */ + public function findReplaceParameters($fileData, $parameters) { + foreach ($parameters as $k => $v) { + if ($v == "true") { + $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + } else if ($v == "false") { + $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + } else { + $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', $v, $fileData); // {{ asdf }} + } + } + return $fileData; + } + + /** + * Helper function to parse the parameters and return them as an array + * @param {String} the parameter string + * + * @return {Array} the keys and values for the parameters + */ + private function parseParameters($string) { + + $parameters = array(); + $betweenSQuotes = false; + $betweenDQuotes = false; + $inKey = true; + $inValue = false; + $char = ""; + $buffer = ""; + $keyBuffer = ""; + $strLength = strlen($string); + + for ($i = 0; $i < $strLength; $i++) { + + $previousChar = $char; + $char = $string[$i]; + + if ($inKey && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { + // if inKey, a quote, and betweenQuotes is false ignore quote, set betweenQuotes to true and empty buffer to kill spaces + ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); + } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { + // if inKey, a quote, betweenQuotes is true, and previous character is \ add to buffer + $buffer .= $char; + } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { + // if inKey, a quote, betweenQuotes is true set betweenQuotes to false, save as key buffer, empty buffer set inKey false + $keyBuffer = $buffer; + $buffer = ""; + $inKey = false; + $betweenSQuotes = false; + $betweenDQuotes = false; + } else if ($inKey && !$betweenDQuotes && !$betweenSQuotes && ($char == ":")) { + // if inKey, a colon, betweenQuotes is false, save as key buffer, empty buffer, set inKey false set inValue true + $keyBuffer = $buffer; + $buffer = ""; + $inKey = false; + $inValue = true; + } else if ($inKey) { + // if inKey add to buffer + $buffer .= $char; + } else if (!$inKey && !$inValue && ($char == ":")) { + // if inKey is false, inValue false, and a colon set inValue true + $inValue = true; + } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { + // if inValue, a quote, and betweenQuote is false set betweenQuotes to true and empty buffer to kill spaces + ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); + } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { + // if inValue, a quote, betweenQuotes is true, and previous character is \ add to buffer + $buffer .= $char; + } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { + // if inValue, a quote, betweenQuotes is true set betweenQuotes to false, save to parameters array, empty buffer, set inValue false + $buffer = str_replace("\\\"","\"",$buffer); + $buffer = str_replace('\\\'','\'',$buffer); + $parameters[trim($keyBuffer)] = trim($buffer); + $buffer = ""; + $inValue = false; + $betweenSQuotes = false; + $betweenDQuotes = false; + } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && ($char == ",")) { + // if inValue, a comman, betweenQuotes is false, save to parameters array, empty buffer, set inValue false, set inKey true + $parameters[trim($keyBuffer)] = trim($buffer); + $buffer = ""; + $inValue = false; + $inKey = true; + } else if ($inValue && (($i + 1) == $strLength)) { + // if inValue and end of the string add to buffer, save to parameters array + $buffer .= $char; + $parameters[trim($keyBuffer)] = trim($buffer); + } else if ($inValue) { + // if inValue add to buffer + $buffer .= $char; + } else if (!$inValue && !$inKey && ($char == ",")) { + // if inValue is false, inKey false, and a comma set inKey true + $inKey = true; + } + } + + return $parameters; + + } + +} diff --git a/core/lib/PatternLab/PatternLoaders/Mustache.php b/core/lib/PatternLab/PatternLoaders/Mustache.php new file mode 100644 index 000000000..416708f74 --- /dev/null +++ b/core/lib/PatternLab/PatternLoaders/Mustache.php @@ -0,0 +1,156 @@ + '.ms', + * ); + * + * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. + * + * @param string $baseDir Base directory containing Mustache template files. + * @param array $options Array of Loader options (default: array()) + */ + public function __construct($baseDir, array $options = array()) { + + $this->baseDir = rtrim(realpath($baseDir), '/'); + + if (!is_dir($this->baseDir)) { + throw new \Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); + } + + if (array_key_exists('extension', $options)) { + if (empty($options['extension'])) { + $this->extension = ''; + } else { + $this->extension = '.' . ltrim($options['extension'], '.'); + } + } + + if (array_key_exists('patternPaths', $options)) { + $this->patternPaths = $options['patternPaths']; + } + + $this->patternLoader = new \PatternLab\PatternLoader($this->patternPaths); + + } + + /** + * Load a Template by name. + * + * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); + * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; + * + * @param string $name + * + * @return string Mustache Template source + */ + public function load($name) { + + if (!isset($this->templates[$name])) { + try { + $this->templates[$name] = $this->loadFile($name); + } catch (Exception $e) { + print "The partial, ".$name.", wasn't found so a pattern failed to build.\n"; + } + } + + return (isset($this->templates[$name])) ? $this->templates[$name] : false; + + } + + /** + * Helper function for loading a Mustache file by name. + * + * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. + * + * @param string $name + * + * @return string Mustache Template source + */ + protected function loadFile($name) { + + // get pattern data + list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); + + // get the real file path for the pattern + $fileName = $this->getFileName($partialName); + + // throw error if path is not found + if (!file_exists($fileName)) { + throw new \Mustache_Exception_UnknownTemplateException($name); + } + + // get the file data + $fileData = file_get_contents($fileName); + + // if the pattern name had a style modifier find & replace it + if (count($styleModifier) > 0) { + $fileData = $this->patternLoader->findReplaceParameters($fileData, $styleModifier); + } + + // if the pattern name had parameters find & replace them + if (count($parameters) > 0) { + $fileData = $this->patternLoader->findReplaceParameters($fileData, $parameters); + } + + return $fileData; + + } + + /** + * Helper function for getting a Mustache template file name. + * @param {String} the pattern type for the pattern + * @param {String} the pattern sub-type + * + * @return {Array} an array of rendered partials that match the given path + */ + protected function getFileName($name) { + + // defaults + $fileName = ""; + $dirSep = DIRECTORY_SEPARATOR; + + // test to see what kind of path was supplied + $posDash = strpos($name,"-"); + $posSlash = strpos($name,$dirSep); + + if (($posSlash === false) && ($posDash !== false)) { + $fileName = $this->baseDir.$dirSep.$this->patternLoader->getPatternFileName($name); + } else { + $fileName = $this->baseDir.$dirSep.$name; + } + + if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { + $fileName .= $this->extension; + } + + return $fileName; + + } + +} From d8d60e3ee81bb78d216339eb4ea26d4c3139414a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:38:01 -0400 Subject: [PATCH 004/166] deleting the old mustache pattern loader --- core/lib/Mustache/Loader/PatternLoader.php | 348 --------------------- 1 file changed, 348 deletions(-) delete mode 100644 core/lib/Mustache/Loader/PatternLoader.php diff --git a/core/lib/Mustache/Loader/PatternLoader.php b/core/lib/Mustache/Loader/PatternLoader.php deleted file mode 100644 index 858127493..000000000 --- a/core/lib/Mustache/Loader/PatternLoader.php +++ /dev/null @@ -1,348 +0,0 @@ -load('foo'); // equivalent to `file_get_contents(dirname(__FILE__).'/views/foo.mustache'); - * - * This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates: - * - * $m = new Mustache(array( - * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), - * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), - * )); - */ -class Mustache_Loader_PatternLoader implements Mustache_Loader -{ - private $baseDir; - private $extension = '.mustache'; - private $templates = array(); - private $patternPaths = array(); - - /** - * Mustache filesystem Loader constructor. - * - * Passing an $options array allows overriding certain Loader options during instantiation: - * - * $options = array( - * // The filename extension used for Mustache templates. Defaults to '.mustache' - * 'extension' => '.ms', - * ); - * - * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. - * - * @param string $baseDir Base directory containing Mustache template files. - * @param array $options Array of Loader options (default: array()) - */ - public function __construct($baseDir, array $options = array()) - { - $this->baseDir = rtrim(realpath($baseDir), '/'); - - if (!is_dir($this->baseDir)) { - throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); - } - - if (array_key_exists('extension', $options)) { - if (empty($options['extension'])) { - $this->extension = ''; - } else { - $this->extension = '.' . ltrim($options['extension'], '.'); - } - } - - if (array_key_exists('patternPaths', $options)) { - $this->patternPaths = $options['patternPaths']; - } - - } - - /** - * Load a Template by name. - * - * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); - * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) - { - if (!isset($this->templates[$name])) { - try { - $this->templates[$name] = $this->loadFile($name); - } catch (Exception $e) { - print "The partial, ".$name.", wasn't found so a pattern failed to build.\n"; - } - } - - return (isset($this->templates[$name])) ? $this->templates[$name] : false; - - } - - /** - * Helper function for loading a Mustache file by name. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - protected function loadFile($name) - { - - // get pattern data - list($partialName,$styleModifier,$parameters) = $this->getPartialInfo($name); - - // get the real file path for the pattern - $fileName = $this->getFileName($partialName); - //print $fileName."\n"; - // throw error if path is not found - if (!file_exists($fileName)) { - throw new Mustache_Exception_UnknownTemplateException($name); - } - - // get the file data - $fileData = file_get_contents($fileName); - - // if the pattern name had a style modifier find & replace it - if (count($styleModifier) > 0) { - $fileData = $this->findReplaceParameters($fileData, $styleModifier); - } - - // if the pattern name had parameters find & replace them - if (count($parameters) > 0) { - $fileData = $this->findReplaceParameters($fileData, $parameters); - } - - return $fileData; - - } - - /** - * Helper function for getting a Mustache template file name. - * @param {String} the pattern type for the pattern - * @param {String} the pattern sub-type - * - * @return {Array} an array of rendered partials that match the given path - */ - protected function getFileName($name) - { - - $fileName = ""; - $dirSep = DIRECTORY_SEPARATOR; - - // test to see what kind of path was supplied - $posDash = strpos($name,"-"); - $posSlash = strpos($name,$dirSep); - if (($posSlash === false) && ($posDash !== false)) { - - list($patternType,$pattern) = $this->getPatternInfo($name); - - // see if the pattern is an exact match for patternPaths. if not iterate over patternPaths to find a likely match - if (isset($this->patternPaths[$patternType][$pattern])) { - $fileName = $this->baseDir.$dirSep.$this->patternPaths[$patternType][$pattern]["patternSrcPath"]; - } else if (isset($this->patternPaths[$patternType])) { - foreach($this->patternPaths[$patternType] as $patternMatchKey=>$patternMatchValue) { - $pos = strpos($patternMatchKey,$pattern); - if ($pos !== false) { - $fileName = $this->baseDir.$dirSep.$patternMatchValue["patternSrcPath"]; - break; - } - } - } - - } else { - $fileName = $this->baseDir.$dirSep.$name; - } - - if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { - $fileName .= $this->extension; - } - - return $fileName; - - } - - /** - * Helper function to return the parts of a partial name - * @param {String} the name of the partial - * - * @return {Array} the pattern type and the name of the pattern - */ - private function getPatternInfo($name) - { - - $patternBits = explode("-",$name); - - $i = 1; - $k = 2; - $c = count($patternBits); - $patternType = $patternBits[0]; - while (!isset($this->patternPaths[$patternType]) && ($i < $c)) { - $patternType .= "-".$patternBits[$i]; - $i++; - $k++; - } - - $patternBits = explode("-",$name,$k); - $pattern = $patternBits[count($patternBits)-1]; - - return array($patternType, $pattern); - - } - - /** - * Helper function for finding if a partial name has style modifier or parameters - * @param {String} the pattern name - * - * @return {Array} an array containing just the partial name, a style modifier, and any parameters - */ - protected function getPartialInfo($partial) - { - - $styleModifier = array(); - $parameters = array(); - - if (strpos($partial, "(") !== false) { - $partialBits = explode("(",$partial,2); - $partial = trim($partialBits[0]); - $parametersString = substr($partialBits[1],0,(strlen($partialBits[1]) - strlen(strrchr($partialBits[1],")")))); - $parameters = $this->parseParameters($parametersString); - } - - if (strpos($partial, ":") !== false) { - $partialBits = explode(":",$partial,2); - $partial = $partialBits[0]; - $styleModifier = array("styleModifier" => $partialBits[1]); - } - - return array($partial,$styleModifier,$parameters); - - } - - /** - * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache - * @param {String} the file contents - * @param {Array} an array of paramters to match - * - * @return {String} the modified file contents - */ - private function findReplaceParameters($fileData, $parameters) - { - foreach ($parameters as $k => $v) { - if ($v == "true") { - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else if ($v == "false") { - $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else { - $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', $v, $fileData); // {{ asdf }} - } - } - return $fileData; - } - - /** - * Helper function to parse the parameters and return them as an array - * @param {String} tbe parameter string - * - * @return {Array} the keys and values for the parameters - */ - private function parseParameters($string) - { - - $parameters = array(); - $betweenSQuotes = false; - $betweenDQuotes = false; - $inKey = true; - $inValue = false; - $char = ""; - $buffer = ""; - $keyBuffer = ""; - $strLength = strlen($string); - - for ($i = 0; $i < $strLength; $i++) { - - $previousChar = $char; - $char = $string[$i]; - - if ($inKey && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { - // if inKey, a quote, and betweenQuotes is false ignore quote, set betweenQuotes to true and empty buffer to kill spaces - ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); - } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { - // if inKey, a quote, betweenQuotes is true, and previous character is \ add to buffer - $buffer .= $char; - } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { - // if inKey, a quote, betweenQuotes is true set betweenQuotes to false, save as key buffer, empty buffer set inKey false - $keyBuffer = $buffer; - $buffer = ""; - $inKey = false; - $betweenSQuotes = false; - $betweenDQuotes = false; - } else if ($inKey && !$betweenDQuotes && !$betweenSQuotes && ($char == ":")) { - // if inKey, a colon, betweenQuotes is false, save as key buffer, empty buffer, set inKey false set inValue true - $keyBuffer = $buffer; - $buffer = ""; - $inKey = false; - $inValue = true; - } else if ($inKey) { - // if inKey add to buffer - $buffer .= $char; - } else if (!$inKey && !$inValue && ($char == ":")) { - // if inKey is false, inValue false, and a colon set inValue true - $inValue = true; - } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { - // if inValue, a quote, and betweenQuote is false set betweenQuotes to true and empty buffer to kill spaces - ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); - } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { - // if inValue, a quote, betweenQuotes is true, and previous character is \ add to buffer - $buffer .= $char; - } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { - // if inValue, a quote, betweenQuotes is true set betweenQuotes to false, save to parameters array, empty buffer, set inValue false - $buffer = str_replace("\\\"","\"",$buffer); - $buffer = str_replace('\\\'','\'',$buffer); - $parameters[trim($keyBuffer)] = trim($buffer); - $buffer = ""; - $inValue = false; - $betweenSQuotes = false; - $betweenDQuotes = false; - } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && ($char == ",")) { - // if inValue, a comman, betweenQuotes is false, save to parameters array, empty buffer, set inValue false, set inKey true - $parameters[trim($keyBuffer)] = trim($buffer); - $buffer = ""; - $inValue = false; - $inKey = true; - } else if ($inValue && (($i + 1) == $strLength)) { - // if inValue and end of the string add to buffer, save to parameters array - $buffer .= $char; - $parameters[trim($keyBuffer)] = trim($buffer); - } else if ($inValue) { - // if inValue add to buffer - $buffer .= $char; - } else if (!$inValue && !$inKey && ($char == ",")) { - // if inValue is false, inKey false, and a comma set inKey true - $inKey = true; - } - } - - return $parameters; - - } - -} From 164872ed8c78000ed345573500b0ce247456c437 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:38:14 -0400 Subject: [PATCH 005/166] referencing the new pattern loader --- core/lib/PatternLab/Builder.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 762990a66..d94d8b0df 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -13,7 +13,6 @@ namespace PatternLab; use \Mustache_Engine as Engine; -use \Mustache_Loader_PatternLoader as PatternLoader; use \Mustache_Loader_FilesystemLoader as FilesystemLoader; class Builder { @@ -81,8 +80,8 @@ public function __construct($config = array()) { */ protected function loadMustachePatternLoaderInstance() { $this->mpl = new Engine(array( - "loader" => new PatternLoader(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), - "partials_loader" => new PatternLoader(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) + "loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), + "partials_loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) )); } From 87d36d997027d1f1f739d14feb4a240a2f884e4c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:39:58 -0400 Subject: [PATCH 006/166] removing the "use" calls --- core/lib/PatternLab/Builder.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index d94d8b0df..0846d010a 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -12,9 +12,6 @@ namespace PatternLab; -use \Mustache_Engine as Engine; -use \Mustache_Loader_FilesystemLoader as FilesystemLoader; - class Builder { /** @@ -79,7 +76,7 @@ public function __construct($config = array()) { * @return {Object} an instance of the Mustache engine */ protected function loadMustachePatternLoaderInstance() { - $this->mpl = new Engine(array( + $this->mpl = new \Mustache_Engine(array( "loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), "partials_loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) )); @@ -91,9 +88,9 @@ protected function loadMustachePatternLoaderInstance() { * @return {Object} an instance of the Mustache engine */ protected function loadMustacheFileSystemLoaderInstance() { - $this->mfs = new Engine(array( - "loader" => new FilesystemLoader(__DIR__."/../../templates/"), - "partials_loader" => new FilesystemLoader(__DIR__."/../../templates/partials/") + $this->mfs = new \Mustache_Engine(array( + "loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/"), + "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/partials/") )); } @@ -103,7 +100,7 @@ protected function loadMustacheFileSystemLoaderInstance() { * @return {Object} an instance of the Mustache engine */ protected function loadMustacheVanillaInstance() { - $this->mv = new Engine; + $this->mv = new \Mustache_Engine; } /** From b5219c82de8bdb5dde90711d20771821c6f465ca Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:49:42 -0400 Subject: [PATCH 007/166] can add multiple classes via style modifiers --- core/lib/PatternLab/PatternLoader.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index 8886d7ceb..c6729c97c 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -103,7 +103,12 @@ public function getPartialInfo($partial) { if (strpos($partial, ":") !== false) { $partialBits = explode(":",$partial,2); $partial = $partialBits[0]; - $styleModifier = array("styleModifier" => $partialBits[1]); + $styleModifier = $partialBits[1]; + if (strpos($styleModifier, "|") !== false) { + $styleModifierBits = explode("|",$styleModifier); + $styleModifier = join(" ",$styleModifierBits); + } + $styleModifier = array("styleModifier" => $styleModifier); } return array($partial,$styleModifier,$parameters); From 5c4c93cc91e67892307b180b570a49078107f9cf Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:58:53 -0400 Subject: [PATCH 008/166] spacing comments --- core/lib/PatternLab/PatternLoader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index c6729c97c..434b6ddf2 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -126,10 +126,10 @@ public function findReplaceParameters($fileData, $parameters) { foreach ($parameters as $k => $v) { if ($v == "true") { $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else if ($v == "false") { $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else { $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', $v, $fileData); // {{ asdf }} } From addfed9116ba4adbe4332ebc8235925b34633d4b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 09:59:13 -0400 Subject: [PATCH 009/166] better matching mustache escaped and unescaped data --- core/lib/PatternLab/PatternLoader.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index 434b6ddf2..be93fbdcd 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -131,7 +131,8 @@ public function findReplaceParameters($fileData, $parameters) { $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else { - $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', $v, $fileData); // {{ asdf }} + $fileData = preg_replace('/{{{([\s]*'.$k .'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} + $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} } } return $fileData; From 1051070dd047d56ff7a739251f45c5aa62097e27 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 12:14:10 -0400 Subject: [PATCH 010/166] adding support for simple lists --- core/lib/PatternLab/PatternLoader.php | 83 +++++++++++++++++++++------ 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index be93fbdcd..f8db72bbf 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -124,7 +124,17 @@ public function getPartialInfo($partial) { */ public function findReplaceParameters($fileData, $parameters) { foreach ($parameters as $k => $v) { - if ($v == "true") { + if (is_array($v)) { + if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { + if (isset($matches[2])) { + $partialData = ""; + foreach ($v as $v2) { + $partialData .= $this->findReplaceParameters($matches[2], $v2); + } + $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); + } + } + } else if ($v == "true") { $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else if ($v == "false") { @@ -146,15 +156,20 @@ public function findReplaceParameters($fileData, $parameters) { */ private function parseParameters($string) { - $parameters = array(); - $betweenSQuotes = false; - $betweenDQuotes = false; - $inKey = true; - $inValue = false; - $char = ""; - $buffer = ""; - $keyBuffer = ""; - $strLength = strlen($string); + $parameters = array(); + $arrayParameters = array(); + $arrayOptions = array(); + $betweenSQuotes = false; + $betweenDQuotes = false; + $inKey = true; + $inValue = false; + $inArray = false; + $inOption = false; + $char = ""; + $buffer = ""; + $keyBuffer = ""; + $arrayKeyBuffer = ""; + $strLength = strlen($string); for ($i = 0; $i < $strLength; $i++) { @@ -186,6 +201,25 @@ private function parseParameters($string) { } else if (!$inKey && !$inValue && ($char == ":")) { // if inKey is false, inValue false, and a colon set inValue true $inValue = true; + } else if ($inValue && !$inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "[")) { + // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer + $inArray = true; + $inValue = false; + $arrayKeyBuffer = trim($keyBuffer); + } else if ($inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "]")) { + // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer + $inArray = false; + $parameters[$arrayKeyBuffer] = $arrayParameters; + $arrayParameters = array(); + } else if ($inArray && !$inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "{")) { + $inOption = true; + $inKey = true; + } else if ($inArray && $inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "}")) { + $inOption = false; + $inValue = false; + $inKey = false; + $arrayParameters[] = $arrayOptions; + $arrayOptions = array(); } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { // if inValue, a quote, and betweenQuote is false set betweenQuotes to true and empty buffer to kill spaces ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); @@ -196,27 +230,44 @@ private function parseParameters($string) { // if inValue, a quote, betweenQuotes is true set betweenQuotes to false, save to parameters array, empty buffer, set inValue false $buffer = str_replace("\\\"","\"",$buffer); $buffer = str_replace('\\\'','\'',$buffer); - $parameters[trim($keyBuffer)] = trim($buffer); - $buffer = ""; - $inValue = false; + if ($inArray) { + $arrayOptions[trim($keyBuffer)] = trim($buffer); + } else { + $parameters[trim($keyBuffer)] = trim($buffer); + } + $buffer = ""; + $inValue = false; $betweenSQuotes = false; $betweenDQuotes = false; } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && ($char == ",")) { // if inValue, a comman, betweenQuotes is false, save to parameters array, empty buffer, set inValue false, set inKey true - $parameters[trim($keyBuffer)] = trim($buffer); + if ($inArray) { + $arrayOptions[trim($keyBuffer)] = trim($buffer); + } else { + $parameters[trim($keyBuffer)] = trim($buffer); + } $buffer = ""; $inValue = false; $inKey = true; } else if ($inValue && (($i + 1) == $strLength)) { // if inValue and end of the string add to buffer, save to parameters array $buffer .= $char; - $parameters[trim($keyBuffer)] = trim($buffer); + if ($inArray) { + $arrayOptions[trim($keyBuffer)] = trim($buffer); + } else { + $parameters[trim($keyBuffer)] = trim($buffer); + } } else if ($inValue) { // if inValue add to buffer $buffer .= $char; } else if (!$inValue && !$inKey && ($char == ",")) { // if inValue is false, inKey false, and a comma set inKey true - $inKey = true; + if ($inArray && !$inOption) { + // don't do anything + } else { + $inKey = true; + } + } } From 4aa170e7066b32bca806054e6d0ca84d366a5e08 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 13:32:04 -0400 Subject: [PATCH 011/166] removing unnecessary set-up of an extension --- core/lib/PatternLab/PatternLoader.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index f8db72bbf..2a122f21e 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -14,7 +14,6 @@ class PatternLoader { - private $extension = '.mustache'; private $patternPaths = array(); /** From 77e2a00da22e227134393304c178c62b18ff0683 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 13:32:30 -0400 Subject: [PATCH 012/166] generalizing the pattern loader instance --- core/lib/PatternLab/Builder.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 0846d010a..382ffd755 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -75,11 +75,17 @@ public function __construct($config = array()) { * * @return {Object} an instance of the Mustache engine */ - protected function loadMustachePatternLoaderInstance() { - $this->mpl = new \Mustache_Engine(array( - "loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), - "partials_loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) - )); + protected function loadPatternLoaderInstance() { + + if ($config["patternEngine"] == "twig") { + + } else { + $this->pl = new \Mustache_Engine(array( + "loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), + "partials_loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) + )); + } + } /** @@ -138,7 +144,7 @@ protected function renderPattern($f,$p) { } - $pattern = $this->mpl->render($f,$d); + $pattern = $this->pl->render($f,$d); $escaped = htmlentities($pattern); if ($this->addPatternHF) { @@ -233,7 +239,7 @@ protected function generatePatterns() { $this->addPatternHF = true; // make sure $this->mpl & $this->mv are refreshed - $this->loadMustachePatternLoaderInstance(); + $this->loadPatternLoaderInstance(); $this->loadMustacheVanillaInstance(); // loop over the pattern paths to generate patterns for each @@ -867,7 +873,7 @@ protected function gatherPatternInfo() { array_walk_recursive($this->d,'PatternLab\Builder::compareReplace'); // make sure $this->mpl is refreshed - $this->loadMustachePatternLoaderInstance(); + $this->loadPatternLoaderInstance(); // run through the nav items and generate pattern partials and the view all pages foreach ($this->navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { From 7c8b2e862148212ba2f1c9e89941bf71fad90424 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 13:32:40 -0400 Subject: [PATCH 013/166] a twig pattern loader class --- core/lib/PatternLab/PatternLoaders/Twig.php | 226 ++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 core/lib/PatternLab/PatternLoaders/Twig.php diff --git a/core/lib/PatternLab/PatternLoaders/Twig.php b/core/lib/PatternLab/PatternLoaders/Twig.php new file mode 100644 index 000000000..4dbf4cbc1 --- /dev/null +++ b/core/lib/PatternLab/PatternLoaders/Twig.php @@ -0,0 +1,226 @@ + + * + */ + +namespace PatternLab\PatternLoaders; + +class Twig implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface { + + /** Identifier of the main namespace. */ + const MAIN_NAMESPACE = '__main__'; + + protected $paths = array(); + protected $patternPaths = array(); + protected $cache = array(); + + /** + * Constructor. + * + * @param string|array $paths A path or an array of paths where to look for templates + */ + public function __construct($paths = array(),$patternPaths = array()) { + if ($paths) { + $this->setPaths($paths); + } + $this->patternPaths = $patternPaths['patternPaths']; + $this->patternLoader = new \PatternLab\PatternLoader($this->patternPaths); + } + + /** + * Returns the paths to the templates. + * + * @param string $namespace A path namespace + * + * @return array The array of paths where to look for templates + */ + public function getPaths($namespace = self::MAIN_NAMESPACE) { + return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array(); + } + + /** + * Returns the path namespaces. + * + * The main namespace is always defined. + * + * @return array The array of defined namespaces + */ + public function getNamespaces() { + return array_keys($this->paths); + } + + /** + * Sets the paths where templates are stored. + * + * @param string|array $paths A path or an array of paths where to look for templates + * @param string $namespace A path namespace + */ + public function setPaths($paths, $namespace = self::MAIN_NAMESPACE) { + if (!is_array($paths)) { + $paths = array($paths); + } + + $this->paths[$namespace] = array(); + foreach ($paths as $path) { + $this->addPath($path, $namespace); + } + } + + /** + * Adds a path where templates are stored. + * + * @param string $path A path where to look for templates + * @param string $namespace A path name + * + * @throws Twig_Error_Loader + */ + public function addPath($path, $namespace = self::MAIN_NAMESPACE) { + // invalidate the cache + $this->cache = array(); + + if (!is_dir($path)) { + throw new \Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + } + + $this->paths[$namespace][] = rtrim($path, '/\\'); + } + + /** + * Prepends a path where templates are stored. + * + * @param string $path A path where to look for templates + * @param string $namespace A path name + * + * @throws Twig_Error_Loader + */ + public function prependPath($path, $namespace = self::MAIN_NAMESPACE) { + // invalidate the cache + $this->cache = array(); + + if (!is_dir($path)) { + throw new \Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); + } + + $path = rtrim($path, '/\\'); + + if (!isset($this->paths[$namespace])) { + $this->paths[$namespace][] = $path; + } else { + array_unshift($this->paths[$namespace], $path); + } + + } + + /** + * {@inheritdoc} + */ + public function getSource($name) { + return file_get_contents($this->findTemplate($name)); + } + + /** + * {@inheritdoc} + */ + public function getCacheKey($name) { + return $this->findTemplate($name); + } + + /** + * {@inheritdoc} + */ + public function exists($name) { + + $name = $this->normalizeName($name); + + if (isset($this->cache[$name])) { + return true; + } + + try { + $this->findTemplate($name); + + return true; + } catch (\Twig_Error_Loader $exception) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function isFresh($name, $time) { + return filemtime($this->findTemplate($name)) <= $time; + } + + protected function findTemplate($name) { + + $name = $this->patternLoader->getFileName($name); + + $name = $this->normalizeName($name); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + $this->validateName($name); + + $namespace = self::MAIN_NAMESPACE; + $shortname = $name; + if (isset($name[0]) && '@' == $name[0]) { + if (false === $pos = strpos($name, '/')) { + throw new \Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); + } + + $namespace = substr($name, 1, $pos - 1); + $shortname = substr($name, $pos + 1); + } + + if (!isset($this->paths[$namespace])) { + throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); + } + + foreach ($this->paths[$namespace] as $path) { + if (is_file($path.'/'.$shortname)) { + return $this->cache[$name] = $path.'/'.$shortname; + } + } + + throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name."f", implode(', ', $this->paths[$namespace]))); + } + + protected function normalizeName($name) { + return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/')); + } + + protected function validateName($name) { + + if (false !== strpos($name, "\0")) { + throw new \Twig_Error_Loader('A template name cannot contain NUL bytes.'); + } + + $name = ltrim($name, '/'); + $parts = explode('/', $name); + $level = 0; + foreach ($parts as $part) { + if ('..' === $part) { + --$level; + } elseif ('.' !== $part) { + ++$level; + } + + if ($level < 0) { + throw new \Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); + } + } + + } + +} From 950e43515ec258abb3764c249557be49e95adb58 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 15:19:55 -0400 Subject: [PATCH 014/166] adding config option to switch patternEngine --- core/config/config.ini.default | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/config/config.ini.default b/core/config/config.ini.default index a5d8a5807..25fa0ec0c 100644 --- a/core/config/config.ini.default +++ b/core/config/config.ini.default @@ -41,3 +41,6 @@ styleGuideExcludes = "" // should the cache buster be on, set to false to set the cacheBuster value to 0 cacheBusterOn = "true" + +// the pattern rending engine +patternEngine = "mustache" \ No newline at end of file From 91e44972b8a11be2f818aa7711ac42f4bec67888 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 15:20:26 -0400 Subject: [PATCH 015/166] support for pattern extensions based on pattern engine --- core/lib/PatternLab/Builder.php | 50 +++++++++++++-------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 382ffd755..ee80e2095 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -68,26 +68,13 @@ public function __construct($config = array()) { $this->enableCSS = false; $this->patternCSS = array(); - } - - /** - * Load a new Mustache instance that uses the Pattern Loader - * - * @return {Object} an instance of the Mustache engine - */ - protected function loadPatternLoaderInstance() { - - if ($config["patternEngine"] == "twig") { - - } else { - $this->pl = new \Mustache_Engine(array( - "loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)), - "partials_loader" => new PatternLoaders\Mustache(__DIR__.$this->sp,array("patternPaths" => $this->patternPaths)) - )); - } + // find the pattern extension + $this->patternExtension = $this->patternEngine; } + + /** * Load a new Mustache instance that uses the File System Loader * @@ -238,8 +225,9 @@ protected function generatePatterns() { // make sure the pattern header & footer are added $this->addPatternHF = true; - // make sure $this->mpl & $this->mv are refreshed - $this->loadPatternLoaderInstance(); + // make sure $this->pl & $this->mv are refreshed + $patternLoader = new PatternLoader($this->patternPaths); + $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); $this->loadMustacheVanillaInstance(); // loop over the pattern paths to generate patterns for each @@ -251,7 +239,7 @@ protected function generatePatterns() { if ($pathInfo["render"]) { // get the rendered, escaped, and mustache pattern - $this->generatePatternFile($pathInfo["patternSrcPath"].".mustache",$pathInfo["patternPartial"],$pathInfo["patternDestPath"],$pathInfo["patternState"]); + $this->generatePatternFile($pathInfo["patternSrcPath"].".".$this->patternExtension,$pathInfo["patternPartial"],$pathInfo["patternDestPath"],$pathInfo["patternState"]); } @@ -294,7 +282,7 @@ private function generatePatternFile($f,$p,$path,$state) { // write out the various pattern files file_put_contents(__DIR__.$this->pp.$path."/".$path.".html",$rf); file_put_contents(__DIR__.$this->pp.$path."/".$path.".escaped.html",$e); - file_put_contents(__DIR__.$this->pp.$path."/".$path.".mustache",$m); + file_put_contents(__DIR__.$this->pp.$path."/".$path.".".$this->patternExtension,$m); if ($this->enableCSS && isset($this->patternCSS[$p])) { file_put_contents(__DIR__.$this->pp.$path."/".$path.".css",htmlentities($this->patternCSS[$p])); } @@ -427,7 +415,7 @@ protected function gatherLineages() { $filename = $patternInfo["patternSrcPath"]; // if a file doesn't exist it assumes it's a pseudo-pattern and will use the last lineage found - if (file_exists(__DIR__.$this->sp.$filename.".mustache")) { + if (file_exists(__DIR__.$this->sp.$filename.".".$this->patternExtension)) { $foundLineages = array_unique($this->getLineage($filename)); } @@ -530,6 +518,7 @@ protected function gatherPatternInfo() { $patternSubtypeSet = false; $dirSep = DIRECTORY_SEPARATOR; + // initialize various arrays $this->navItems = array(); $this->navItems["patternTypes"] = array(); @@ -608,7 +597,7 @@ protected function gatherPatternInfo() { // starting a new set of pattern types. it might not have any pattern subtypes $patternSubtypeSet = true; - } else if ($object->isFile() && ($object->getExtension() == "mustache")) { + } else if ($object->isFile() && ($object->getExtension() == $this->patternExtension)) { /************************************* * This section is for: @@ -616,7 +605,7 @@ protected function gatherPatternInfo() { *************************************/ $patternFull = $object->getFilename(); // 00-colors.mustache - $pattern = str_replace(".mustache","",$patternFull); // 00-colors + $pattern = str_replace(".".$this->patternExtension,"",$patternFull); // 00-colors // check for pattern state $patternState = ""; @@ -674,7 +663,7 @@ protected function gatherPatternInfo() { } // add all patterns to patternPaths - $patternSrcPath = str_replace(__DIR__.$this->sp,"",str_replace(".mustache","",$object->getPathname())); + $patternSrcPath = str_replace(__DIR__.$this->sp,"",str_replace(".".$this->patternExtension,"",$object->getPathname())); $patternDestPath = $patternPathDash; $this->patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, "patternDestPath" => $patternDestPath, @@ -706,7 +695,7 @@ protected function gatherPatternInfo() { // set-up the names // $patternFull is defined above 00-colors.mustache $patternBits = explode("~",$patternFull); - $patternBase = $patternBits[0].".mustache"; // 00-homepage.mustache + $patternBase = $patternBits[0].".".$this->patternExtension; // 00-homepage.mustache $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage $patternBaseJSON = $patternBits[0].".json"; // 00-homepage.json $stripJSON = str_replace(".json","",$patternBits[1]); @@ -738,7 +727,7 @@ protected function gatherPatternInfo() { // set-up the info for the nav $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", - "patternSrcPath" => str_replace(__DIR__.$this->sp,"",preg_replace("/\~(.*)\.json/",".mustache",$object->getPathname())), + "patternSrcPath" => str_replace(__DIR__.$this->sp,"",preg_replace("/\~(.*)\.json/",".".$this->patternExtension,$object->getPathname())), "patternName" => ucwords($patternClean), "patternState" => $patternState, "patternPartial" => $patternPartial); @@ -873,7 +862,8 @@ protected function gatherPatternInfo() { array_walk_recursive($this->d,'PatternLab\Builder::compareReplace'); // make sure $this->mpl is refreshed - $this->loadPatternLoaderInstance(); + $patternLoader = new PatternLoader($this->patternPaths); + $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); // run through the nav items and generate pattern partials and the view all pages foreach ($this->navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { @@ -997,7 +987,7 @@ protected function gatherPatternInfo() { * @return {Array} a list of patterns */ protected function getLineage($filename) { - $data = file_get_contents(__DIR__.$this->sp.$filename.".mustache"); + $data = file_get_contents(__DIR__.$this->sp.$filename.".".$this->patternExtension); //$data = file_get_contents($filename); if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)}}/',$data,$matches)) { return $matches[2]; @@ -1059,7 +1049,7 @@ protected function getListItems($filepath) { * @return {String} the directory for the pattern */ protected function getPath($filepath,$type = "m") { - $file = ($type == 'm') ? '\.mustache' : '\.json'; + $file = ($type == 'm') ? '\.'.$this->patternExtension : '\.json'; if (preg_match('/\/('.$this->patternTypesRegex.'\/(([A-z0-9-]{1,})\/|)([A-z0-9-]{1,}))'.$file.'$/',$filepath,$matches)) { return $matches[1]; } From 730c3b957bea13e2c5731133ee79b8cbaa05d93e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 15:22:11 -0400 Subject: [PATCH 016/166] load the pattern engine in the loader --- core/lib/PatternLab/PatternLoader.php | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index 2a122f21e..c02a221c5 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -25,6 +25,34 @@ public function __construct($patternPaths) { } + /** + * Load a new instance that of the Pattern Loader + * + * @return {Object} an instance of the Mustache engine + */ + public function loadPatternLoaderInstance($patternEngine,$sourcePatternsPath) { + + if ($patternEngine == "twig") { + + $loader = new \SplClassLoader('Twig', __DIR__.'/../../lib'); + $loader->setNamespaceSeparator("_"); + $loader->register(); + + $loader = new PatternLoaders\Twig($sourcePatternsPath,array("patternPaths" => $this->patternPaths)); + $instance = new \Twig_Environment($loader); + + } else { + + $instance = new \Mustache_Engine(array( + "loader" => new PatternLoaders\Mustache($sourcePatternsPath,array("patternPaths" => $this->patternPaths)), + "partials_loader" => new PatternLoaders\Mustache($sourcePatternsPath,array("patternPaths" => $this->patternPaths)) + )); + + } + + return $instance; + } + /** * Helper function to return the pattern file name * @param {String} the name of the pattern From 14e6de29e9fe5e332aab77d9f0f3494f87122fcb Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 15:22:22 -0400 Subject: [PATCH 017/166] pattern lab twig loader --- core/lib/PatternLab/PatternLoaders/Twig.php | 41 +++++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/core/lib/PatternLab/PatternLoaders/Twig.php b/core/lib/PatternLab/PatternLoaders/Twig.php index 4dbf4cbc1..0a6bce2a8 100644 --- a/core/lib/PatternLab/PatternLoaders/Twig.php +++ b/core/lib/PatternLab/PatternLoaders/Twig.php @@ -18,9 +18,10 @@ class Twig implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface { /** Identifier of the main namespace. */ const MAIN_NAMESPACE = '__main__'; - protected $paths = array(); + protected $paths = array(); + protected $cache = array(); protected $patternPaths = array(); - protected $cache = array(); + protected $extension = '.twig'; /** * Constructor. @@ -162,7 +163,9 @@ public function isFresh($name, $time) { protected function findTemplate($name) { - $name = $this->patternLoader->getFileName($name); + list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); + + $name = $this->getFileName($partialName); $name = $this->normalizeName($name); @@ -193,7 +196,8 @@ protected function findTemplate($name) { } } - throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name."f", implode(', ', $this->paths[$namespace]))); + + throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]))); } protected function normalizeName($name) { @@ -223,4 +227,33 @@ protected function validateName($name) { } + /** + * Helper function for getting a Mustache template file name. + * @param {String} the pattern type for the pattern + * @param {String} the pattern sub-type + * + * @return {Array} an array of rendered partials that match the given path + */ + protected function getFileName($name) { + + $fileName = ""; + $dirSep = DIRECTORY_SEPARATOR; + + // test to see what kind of path was supplied + $posDash = strpos($name,"-"); + $posSlash = strpos($name,$dirSep); + + if (($posSlash === false) && ($posDash !== false)) { + $fileName = $this->patternLoader->getPatternFileName($name); + } else { + $fileName = $name; + } + + if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { + $fileName .= $this->extension; + } + + return $fileName; + + } } From 408c83fbc878a38e7def981e760fa714c46e0f23 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sat, 19 Apr 2014 15:49:06 -0400 Subject: [PATCH 018/166] cmd+a will only be turned off when code view is up --- core/styleguide/js/code-pattern.js | 8 +++++--- core/styleguide/js/code-viewer.js | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/core/styleguide/js/code-pattern.js b/core/styleguide/js/code-pattern.js index f8a949519..aa11c319a 100644 --- a/core/styleguide/js/code-pattern.js +++ b/core/styleguide/js/code-pattern.js @@ -91,9 +91,11 @@ jwerty.key('ctrl+shift+c', function (e) { // when the code panel is open hijack cmd+a so that it only selects the code view jwerty.key('cmd+a/ctrl+a', function (e) { - var obj = JSON.stringify({ "keyPress": "cmd+a" }); - parent.postMessage(obj,codePattern.targetOrigin); - return false; + if (codePattern.codeOverlayActive) { + var obj = JSON.stringify({ "keyPress": "cmd+a" }); + parent.postMessage(obj,codePattern.targetOrigin); + return false; + } }); // open the mustache panel diff --git a/core/styleguide/js/code-viewer.js b/core/styleguide/js/code-viewer.js index e245f6347..f8754ec4e 100644 --- a/core/styleguide/js/code-viewer.js +++ b/core/styleguide/js/code-viewer.js @@ -412,8 +412,10 @@ jwerty.key('ctrl+shift+c', function (e) { // when the code panel is open hijack cmd+a so that it only selects the code view jwerty.key('cmd+a/ctrl+a', function (e) { - codeViewer.selectCode(); - return false; + if (codeViewer.codeActive) { + codeViewer.selectCode(); + return false; + } }); // open the mustache panel From 52afbd205e62de3fcf33312ae1d284996b98d31a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 08:54:58 -0400 Subject: [PATCH 019/166] adding a JSON linter to give better errors on syntax errors --- core/lib/Seld/JsonLint/JsonParser.php | 464 ++++++++++++++++++++ core/lib/Seld/JsonLint/LICENSE | 19 + core/lib/Seld/JsonLint/Lexer.php | 229 ++++++++++ core/lib/Seld/JsonLint/ParsingException.php | 28 ++ core/lib/Seld/JsonLint/Undefined.php | 16 + 5 files changed, 756 insertions(+) create mode 100755 core/lib/Seld/JsonLint/JsonParser.php create mode 100755 core/lib/Seld/JsonLint/LICENSE create mode 100755 core/lib/Seld/JsonLint/Lexer.php create mode 100755 core/lib/Seld/JsonLint/ParsingException.php create mode 100755 core/lib/Seld/JsonLint/Undefined.php diff --git a/core/lib/Seld/JsonLint/JsonParser.php b/core/lib/Seld/JsonLint/JsonParser.php new file mode 100755 index 000000000..d94d6bf49 --- /dev/null +++ b/core/lib/Seld/JsonLint/JsonParser.php @@ -0,0 +1,464 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\JsonLint; + +use stdClass; + +/** + * Parser class + * + * Example: + * + * $parser = new JsonParser(); + * // returns null if it's valid json, or an error object + * $parser->lint($json); + * // returns parsed json, like json_decode does, but slower, throws exceptions on failure. + * $parser->parse($json); + * + * Ported from https://github.com/zaach/jsonlint + */ +class JsonParser +{ + const DETECT_KEY_CONFLICTS = 1; + const ALLOW_DUPLICATE_KEYS = 2; + + private $flags; + private $stack; + private $vstack; // semantic value stack + private $lstack; // location stack + + private $yy; + private $symbols = array( + 'error' => 2, + 'JSONString' => 3, + 'STRING' => 4, + 'JSONNumber' => 5, + 'NUMBER' => 6, + 'JSONNullLiteral' => 7, + 'NULL' => 8, + 'JSONBooleanLiteral' => 9, + 'TRUE' => 10, + 'FALSE' => 11, + 'JSONText' => 12, + 'JSONValue' => 13, + 'EOF' => 14, + 'JSONObject' => 15, + 'JSONArray' => 16, + '{' => 17, + '}' => 18, + 'JSONMemberList' => 19, + 'JSONMember' => 20, + ':' => 21, + ',' => 22, + '[' => 23, + ']' => 24, + 'JSONElementList' => 25, + '$accept' => 0, + '$end' => 1, + ); + + private $terminals_ = array( + 2 => "error", + 4 => "STRING", + 6 => "NUMBER", + 8 => "NULL", + 10 => "TRUE", + 11 => "FALSE", + 14 => "EOF", + 17 => "{", + 18 => "}", + 21 => ":", + 22 => ",", + 23 => "[", + 24 => "]", + ); + + private $productions_ = array( + 0, + array(3, 1), + array(5, 1), + array(7, 1), + array(9, 1), + array(9, 1), + array(12, 2), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(13, 1), + array(15, 2), + array(15, 3), + array(20, 3), + array(19, 1), + array(19, 3), + array(16, 2), + array(16, 3), + array(25, 1), + array(25, 3) + ); + + private $table = array(array(3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 12 => 1, 13 => 2, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 1 => array(3)), array( 14 => array(1,16)), array( 14 => array(2,7), 18 => array(2,7), 22 => array(2,7), 24 => array(2,7)), array( 14 => array(2,8), 18 => array(2,8), 22 => array(2,8), 24 => array(2,8)), array( 14 => array(2,9), 18 => array(2,9), 22 => array(2,9), 24 => array(2,9)), array( 14 => array(2,10), 18 => array(2,10), 22 => array(2,10), 24 => array(2,10)), array( 14 => array(2,11), 18 => array(2,11), 22 => array(2,11), 24 => array(2,11)), array( 14 => array(2,12), 18 => array(2,12), 22 => array(2,12), 24 => array(2,12)), array( 14 => array(2,3), 18 => array(2,3), 22 => array(2,3), 24 => array(2,3)), array( 14 => array(2,4), 18 => array(2,4), 22 => array(2,4), 24 => array(2,4)), array( 14 => array(2,5), 18 => array(2,5), 22 => array(2,5), 24 => array(2,5)), array( 14 => array(2,1), 18 => array(2,1), 21 => array(2,1), 22 => array(2,1), 24 => array(2,1)), array( 14 => array(2,2), 18 => array(2,2), 22 => array(2,2), 24 => array(2,2)), array( 3 => 20, 4 => array(1,12), 18 => array(1,17), 19 => 18, 20 => 19 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 23, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15), 24 => array(1,21), 25 => 22 ), array( 1 => array(2,6)), array( 14 => array(2,13), 18 => array(2,13), 22 => array(2,13), 24 => array(2,13)), array( 18 => array(1,24), 22 => array(1,25)), array( 18 => array(2,16), 22 => array(2,16)), array( 21 => array(1,26)), array( 14 => array(2,18), 18 => array(2,18), 22 => array(2,18), 24 => array(2,18)), array( 22 => array(1,28), 24 => array(1,27)), array( 22 => array(2,20), 24 => array(2,20)), array( 14 => array(2,14), 18 => array(2,14), 22 => array(2,14), 24 => array(2,14)), array( 3 => 20, 4 => array(1,12), 20 => 29 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 30, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 14 => array(2,19), 18 => array(2,19), 22 => array(2,19), 24 => array(2,19)), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 31, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 18 => array(2,17), 22 => array(2,17)), array( 18 => array(2,15), 22 => array(2,15)), array( 22 => array(2,21), 24 => array(2,21)), + ); + + private $defaultActions = array( + 16 => array(2, 6) + ); + + /** + * @param string $input JSON string + * @return null|ParsingException null if no error is found, a ParsingException containing all details otherwise + */ + public function lint($input) + { + try { + $this->parse($input); + } catch (ParsingException $e) { + return $e; + } + } + + /** + * @param string $input JSON string + * @return mixed + * @throws ParsingException + */ + public function parse($input, $flags = 0) + { + $this->failOnBOM($input); + + $this->flags = $flags; + + $this->stack = array(0); + $this->vstack = array(null); + $this->lstack = array(); + + $yytext = ''; + $yylineno = 0; + $yyleng = 0; + $recovering = 0; + $TERROR = 2; + $EOF = 1; + + $this->lexer = new Lexer(); + $this->lexer->setInput($input); + + $yyloc = $this->lexer->yylloc; + $this->lstack[] = $yyloc; + + $symbol = null; + $preErrorSymbol = null; + $state = null; + $action = null; + $a = null; + $r = null; + $yyval = new stdClass; + $p = null; + $len = null; + $newState = null; + $expected = null; + $errStr = null; + + while (true) { + // retreive state number from top of stack + $state = $this->stack[count($this->stack)-1]; + + // use default actions if available + if (isset($this->defaultActions[$state])) { + $action = $this->defaultActions[$state]; + } else { + if ($symbol == null) { + $symbol = $this->lex(); + } + // read action for current state and first input + $action = isset($this->table[$state][$symbol]) ? $this->table[$state][$symbol] : false; + } + + // handle parse error + if (!$action || !$action[0]) { + if (!$recovering) { + // Report error + $expected = array(); + foreach ($this->table[$state] as $p => $ignore) { + if (isset($this->terminals_[$p]) && $p > 2) { + $expected[] = "'" . $this->terminals_[$p] . "'"; + } + } + + $message = null; + if (in_array("'STRING'", $expected) && in_array(substr($this->lexer->match, 0, 1), array('"', "'"))) { + $message = "Invalid string"; + if ("'" === substr($this->lexer->match, 0, 1)) { + $message .= ", it appears you used single quotes instead of double quotes"; + } elseif (preg_match('{".+?(\\\\[^"bfnrt/\\\\u])}', $this->lexer->getUpcomingInput(), $match)) { + $message .= ", it appears you have an unescaped backslash at: ".$match[1]; + } elseif (preg_match('{"(?:[^"]+|\\\\")*$}m', $this->lexer->getUpcomingInput())) { + $message .= ", it appears you forgot to terminated the string, or attempted to write a multiline string which is invalid"; + } + } + + $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; + $errStr .= $this->lexer->showPosition() . "\n"; + if ($message) { + $errStr .= $message; + } else { + $errStr .= (count($expected) > 1) ? "Expected one of: " : "Expected: "; + $errStr .= implode(', ', $expected); + } + + if (',' === substr(trim($this->lexer->getPastInput()), -1)) { + $errStr .= " - It appears you have an extra trailing comma"; + } + + $this->parseError($errStr, array( + 'text' => $this->lexer->match, + 'token' => !empty($this->terminals_[$symbol]) ? $this->terminals_[$symbol] : $symbol, + 'line' => $this->lexer->yylineno, + 'loc' => $yyloc, + 'expected' => $expected, + )); + } + + // just recovered from another error + if ($recovering == 3) { + if ($symbol == $EOF) { + throw new ParsingException($errStr ?: 'Parsing halted.'); + } + + // discard current lookahead and grab another + $yyleng = $this->lexer->yyleng; + $yytext = $this->lexer->yytext; + $yylineno = $this->lexer->yylineno; + $yyloc = $this->lexer->yylloc; + $symbol = $this->lex(); + } + + // try to recover from error + while (true) { + // check for error recovery rule in this state + if (array_key_exists($TERROR, $this->table[$state])) { + break; + } + if ($state == 0) { + throw new ParsingException($errStr ?: 'Parsing halted.'); + } + $this->popStack(1); + $state = $this->stack[count($this->stack)-1]; + } + + $preErrorSymbol = $symbol; // save the lookahead token + $symbol = $TERROR; // insert generic error symbol as new lookahead + $state = $this->stack[count($this->stack)-1]; + $action = isset($this->table[$state][$TERROR]) ? $this->table[$state][$TERROR] : false; + $recovering = 3; // allow 3 real symbols to be shifted before reporting a new error + } + + // this shouldn't happen, unless resolve defaults are off + if (is_array($action[0]) && count($action) > 1) { + throw new ParsingException('Parse Error: multiple actions possible at state: ' . $state . ', token: ' . $symbol); + } + + switch ($action[0]) { + case 1: // shift + $this->stack[] = $symbol; + $this->vstack[] = $this->lexer->yytext; + $this->lstack[] = $this->lexer->yylloc; + $this->stack[] = $action[1]; // push state + $symbol = null; + if (!$preErrorSymbol) { // normal execution/no error + $yyleng = $this->lexer->yyleng; + $yytext = $this->lexer->yytext; + $yylineno = $this->lexer->yylineno; + $yyloc = $this->lexer->yylloc; + if ($recovering > 0) { + $recovering--; + } + } else { // error just occurred, resume old lookahead f/ before error + $symbol = $preErrorSymbol; + $preErrorSymbol = null; + } + break; + + case 2: // reduce + $len = $this->productions_[$action[1]][1]; + + // perform semantic action + $yyval->token = $this->vstack[count($this->vstack) - $len]; // default to $$ = $1 + // default location, uses first token for firsts, last for lasts + $yyval->store = array( // _$ = store + 'first_line' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_line'], + 'last_line' => $this->lstack[count($this->lstack) - 1]['last_line'], + 'first_column' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_column'], + 'last_column' => $this->lstack[count($this->lstack) - 1]['last_column'], + ); + $r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack, $this->lstack); + + if (!$r instanceof Undefined) { + return $r; + } + + if ($len) { + $this->popStack($len); + } + + $this->stack[] = $this->productions_[$action[1]][0]; // push nonterminal (reduce) + $this->vstack[] = $yyval->token; + $this->lstack[] = $yyval->store; + $newState = $this->table[$this->stack[count($this->stack)-2]][$this->stack[count($this->stack)-1]]; + $this->stack[] = $newState; + break; + + case 3: // accept + + return true; + } + } + + return true; + } + + protected function parseError($str, $hash) + { + throw new ParsingException($str, $hash); + } + + // $$ = $tokens // needs to be passed by ref? + // $ = $token + // _$ removed, useless? + private function performAction(stdClass $yyval, $yytext, $yyleng, $yylineno, $yystate, &$tokens) + { + // $0 = $len + $len = count($tokens) - 1; + switch ($yystate) { + case 1: + $yytext = preg_replace_callback('{(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})}', array($this, 'stringInterpolation'), $yytext); + $yyval->token = $yytext; + break; + case 2: + if (strpos($yytext, 'e') !== false || strpos($yytext, 'E') !== false) { + $yyval->token = floatval($yytext); + } else { + $yyval->token = strpos($yytext, '.') === false ? intval($yytext) : floatval($yytext); + } + break; + case 3: + $yyval->token = null; + break; + case 4: + $yyval->token = true; + break; + case 5: + $yyval->token = false; + break; + case 6: + return $yyval->token = $tokens[$len-1]; + case 13: + $yyval->token = new stdClass; + break; + case 14: + $yyval->token = $tokens[$len-1]; + break; + case 15: + $yyval->token = array($tokens[$len-2], $tokens[$len]); + break; + case 16: + $yyval->token = new stdClass; + $property = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; + $yyval->token->$property = $tokens[$len][1]; + break; + case 17: + $yyval->token = $tokens[$len-2]; + $key = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; + if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2]->{$key})) { + $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; + $errStr .= $this->lexer->showPosition() . "\n"; + $errStr .= "Duplicate key: ".$tokens[$len][0]; + throw new ParsingException($errStr); + } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len-2]->{$key})) { + $duplicateCount = 1; + do { + $duplicateKey = $key . '.' . $duplicateCount++; + } while (isset($tokens[$len-2]->$duplicateKey)); + $key = $duplicateKey; + } + $tokens[$len-2]->$key = $tokens[$len][1]; + break; + case 18: + $yyval->token = array(); + break; + case 19: + $yyval->token = $tokens[$len-1]; + break; + case 20: + $yyval->token = array($tokens[$len]); + break; + case 21: + $tokens[$len-2][] = $tokens[$len]; + $yyval->token = $tokens[$len-2]; + break; + } + + return new Undefined(); + } + + private function stringInterpolation($match) + { + switch ($match[0]) { + case '\\\\': + return '\\'; + case '\"': + return '"'; + case '\b': + return chr(8); + case '\f': + return chr(12); + case '\n': + return "\n"; + case '\r': + return "\r"; + case '\t': + return "\t"; + case '\/': + return "/"; + default: + return html_entity_decode('&#x'.ltrim(substr($match[0], 2), '0').';', 0, 'UTF-8'); + } + } + + private function popStack($n) + { + $this->stack = array_slice($this->stack, 0, - (2 * $n)); + $this->vstack = array_slice($this->vstack, 0, - $n); + $this->lstack = array_slice($this->lstack, 0, - $n); + } + + private function lex() + { + $token = $this->lexer->lex() ?: 1; // $end = 1 + // if token isn't its numeric value, convert + if (!is_numeric($token)) { + $token = isset($this->symbols[$token]) ? $this->symbols[$token] : $token; + } + + return $token; + } + + private function failOnBOM($input) + { + // UTF-8 ByteOrderMark sequence + $bom = "\xEF\xBB\xBF"; + + if (substr($input, 0, 3) === $bom) { + $this->parseError("BOM detected, make sure your input does not include a Unicode Byte-Order-Mark", array()); + } + } +} diff --git a/core/lib/Seld/JsonLint/LICENSE b/core/lib/Seld/JsonLint/LICENSE new file mode 100755 index 000000000..c2344874d --- /dev/null +++ b/core/lib/Seld/JsonLint/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/Seld/JsonLint/Lexer.php b/core/lib/Seld/JsonLint/Lexer.php new file mode 100755 index 000000000..a982c498c --- /dev/null +++ b/core/lib/Seld/JsonLint/Lexer.php @@ -0,0 +1,229 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\JsonLint; + +/** + * Lexer class + * + * Ported from https://github.com/zaach/jsonlint + */ +class Lexer +{ + private $EOF = 1; + private $rules = array( + 0 => '/^\s+/', + 1 => '/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][+-]?[0-9]+)?\b/', + 2 => '{^"(\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"}', + 3 => '/^\{/', + 4 => '/^\}/', + 5 => '/^\[/', + 6 => '/^\]/', + 7 => '/^,/', + 8 => '/^:/', + 9 => '/^true\b/', + 10 => '/^false\b/', + 11 => '/^null\b/', + 12 => '/^$/', + 13 => '/^./', + ); + + private $conditions = array( + "INITIAL" => array( + "rules" => array(0,1,2,3,4,5,6,7,8,9,10,11,12,13), + "inclusive" => true, + ), + ); + + private $conditionStack; + private $input; + private $more; + private $done; + private $matched; + + public $match; + public $yylineno; + public $yyleng; + public $yytext; + public $yylloc; + + public function lex() + { + $r = $this->next(); + if (!$r instanceof Undefined) { + return $r; + } + + return $this->lex(); + } + + public function setInput($input) + { + $this->input = $input; + $this->more = false; + $this->done = false; + $this->yylineno = $this->yyleng = 0; + $this->yytext = $this->matched = $this->match = ''; + $this->conditionStack = array('INITIAL'); + $this->yylloc = array('first_line' => 1, 'first_column' => 0, 'last_line' => 1, 'last_column' => 0); + + return $this; + } + + public function showPosition() + { + $pre = str_replace("\n", '', $this->getPastInput()); + $c = str_repeat('-', strlen($pre)); // new Array(pre.length + 1).join("-"); + + return $pre . str_replace("\n", '', $this->getUpcomingInput()) . "\n" . $c . "^"; + } + + public function getPastInput() + { + $past = substr($this->matched, 0, strlen($this->matched) - strlen($this->match)); + + return (strlen($past) > 20 ? '...' : '') . substr($past, -20); + } + + public function getUpcomingInput() + { + $next = $this->match; + if (strlen($next) < 20) { + $next .= substr($this->input, 0, 20 - strlen($next)); + } + + return substr($next, 0, 20) . (strlen($next) > 20 ? '...' : ''); + } + + protected function parseError($str, $hash) + { + throw new \Exception($str); + } + + private function next() + { + if ($this->done) { + return $this->EOF; + } + if (!$this->input) { + $this->done = true; + } + + $token = null; + $match = null; + $col = null; + $lines = null; + + if (!$this->more) { + $this->yytext = ''; + $this->match = ''; + } + + $rules = $this->getCurrentRules(); + $rulesLen = count($rules); + + for ($i=0; $i < $rulesLen; $i++) { + if (preg_match($this->rules[$rules[$i]], $this->input, $match)) { + preg_match_all('/\n.*/', $match[0], $lines); + $lines = $lines[0]; + if ($lines) { + $this->yylineno += count($lines); + } + + $this->yylloc = array( + 'first_line' => $this->yylloc['last_line'], + 'last_line' => $this->yylineno+1, + 'first_column' => $this->yylloc['last_column'], + 'last_column' => $lines ? strlen($lines[count($lines) - 1]) - 1 : $this->yylloc['last_column'] + strlen($match[0]), + ); + $this->yytext .= $match[0]; + $this->match .= $match[0]; + $this->matches = $match; + $this->yyleng = strlen($this->yytext); + $this->more = false; + $this->input = substr($this->input, strlen($match[0])); + $this->matched .= $match[0]; + $token = $this->performAction($rules[$i], $this->conditionStack[count($this->conditionStack)-1]); + if ($token) { + return $token; + } + + return new Undefined(); + } + } + + if ($this->input === "") { + return $this->EOF; + } + + $this->parseError( + 'Lexical error on line ' . ($this->yylineno+1) . ". Unrecognized text.\n" . $this->showPosition(), + array( + 'text' => "", + 'token' => null, + 'line' => $this->yylineno, + ) + ); + } + + private function begin($condition) + { + $this->conditionStack[] = $condition; + } + + private function popState() + { + return array_pop($this->conditionStack); + } + + private function getCurrentRules() + { + return $this->conditions[$this->conditionStack[count($this->conditionStack)-1]]['rules']; + } + + private function performAction($avoiding_name_collisions, $YY_START) + { + $YYSTATE = $YY_START; + switch ($avoiding_name_collisions) { + case 0:/* skip whitespace */ + break; + case 1: + return 6; + break; + case 2: + $this->yytext = substr($this->yytext, 1, $this->yyleng-2); + + return 4; + case 3: + return 17; + case 4: + return 18; + case 5: + return 23; + case 6: + return 24; + case 7: + return 22; + case 8: + return 21; + case 9: + return 10; + case 10: + return 11; + case 11: + return 8; + case 12: + return 14; + case 13: + return 'INVALID'; + } + } +} diff --git a/core/lib/Seld/JsonLint/ParsingException.php b/core/lib/Seld/JsonLint/ParsingException.php new file mode 100755 index 000000000..6562ef5ea --- /dev/null +++ b/core/lib/Seld/JsonLint/ParsingException.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\JsonLint; + +class ParsingException extends \Exception +{ + protected $details; + + public function __construct($message, $details = array()) + { + $this->details = $details; + parent::__construct($message); + } + + public function getDetails() + { + return $this->details; + } +} diff --git a/core/lib/Seld/JsonLint/Undefined.php b/core/lib/Seld/JsonLint/Undefined.php new file mode 100755 index 000000000..e83d5be69 --- /dev/null +++ b/core/lib/Seld/JsonLint/Undefined.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Seld\JsonLint; + +class Undefined +{ +} From 00b984a6f084b8cc5d36cb1c0aa09ff3c11765a4 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 08:55:18 -0400 Subject: [PATCH 020/166] reworking the json error message handling --- core/lib/PatternLab/Builder.php | 67 +++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index ee80e2095..1c86de92a 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -373,21 +373,25 @@ protected function gatherData() { // gather the data from the main source data.json if (file_exists($this->sd."/_data/_data.json")) { - $this->d = json_decode(file_get_contents($this->sd."/_data/_data.json"),true); - $this->jsonLastErrorMsg("_data/_data.json"); + $data = file_get_contents($this->sd."/_data/_data.json"); + $this->d = json_decode($data,true); + if ($jsonErrorMessage = $this->jsonHasError()) { + $this->jsonLastErrorMsg("_data/_data.json",$jsonErrorMessage,$data); + } } else { print "Missing a required file, source/_data/_data.json. Aborting.\n"; exit; } - $reservedKeys = array("listItems","cacheBuster","link","patternSpecific"); - foreach ($reservedKeys as $reservedKey) { - if (array_key_exists($reservedKey,$this->d)) { - print "\"".$reservedKey."\" is a reserved key in Pattern Lab. The data using that key in _data.json will be overwritten. Please choose a new key.\n"; + if (is_array($this->d)) { + $reservedKeys = array("listItems","cacheBuster","link","patternSpecific"); + foreach ($reservedKeys as $reservedKey) { + if (array_key_exists($reservedKey,$this->d)) { + print "\"".$reservedKey."\" is a reserved key in Pattern Lab. The data using that key in _data.json will be overwritten. Please choose a new key.\n"; + } } } - $this->d["listItems"] = $this->getListItems($this->sd."/_data/_listitems.json"); $this->d["cacheBuster"] = $this->cacheBuster; $this->d["link"] = array(); @@ -745,13 +749,19 @@ protected function gatherPatternInfo() { // get the base data $patternDataBase = array(); if (file_exists($object->getPath()."/".$patternBaseJSON)) { - $patternDataBase = json_decode(file_get_contents($object->getPath()."/".$patternBaseJSON),true); - $this->jsonLastErrorMsg($patternBaseJSON); + $data = file_get_contents($object->getPath()."/".$patternBaseJSON); + $patternDataBase = json_decode($data,true); + if ($jsonErrorMessage = $this->jsonHasError()) { + $this->jsonLastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); + } } // get the special pattern data - $patternData = (array) json_decode(file_get_contents($object->getPathname())); - $this->jsonLastErrorMsg($object->getFilename()); + $data = file_get_contents($object->getPathname()); + $patternData = (array) json_decode($data); + if ($jsonErrorMessage = $this->jsonHasError()) { + $this->jsonLastErrorMsg($object->getFilename(),$jsonErrorMessage,$data); + } // merge them for the file if (!isset($this->d["patternSpecific"][$patternPartial])) { @@ -789,8 +799,11 @@ protected function gatherPatternInfo() { $patternData = $this->getListItems($object->getPathname()); $this->d["patternSpecific"][$patternPartial]["listItems"] = $patternData; } else { - $patternData = json_decode(file_get_contents($object->getPathname()),true); - $this->jsonLastErrorMsg($patternFull); + $data = file_get_contents($object->getPathname()); + $patternData = json_decode($data,true); + if ($jsonErrorMessage = $this->jsonHasError()) { + $this->jsonLastErrorMsg($object->getFilename(),$jsonErrorMessage,$data); + } $this->d["patternSpecific"][$patternPartial]["data"] = $patternData; } @@ -1008,8 +1021,11 @@ protected function getListItems($filepath) { // add list item data, makes 'listItems' a reserved word if (file_exists($filepath)) { - $listItemsJSON = json_decode(file_get_contents($filepath), true); - $this->jsonLastErrorMsg(str_replace($this->sd."/","",$filepath)); + $data = file_get_contents($filepath); + $listItemsJSON = json_decode($data, true); + if ($jsonErrorMessage = $this->jsonHasError()) { + $this->jsonLastErrorMsg(str_replace($this->sd."/","",$filepath),$jsonErrorMessage,$data); + } $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); @@ -1311,9 +1327,9 @@ protected function initializeCSSRuleSaver() { * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 * @param {String} the file that generated the error */ - protected function jsonLastErrorMsg($file) { + protected function jsonHasError() { $errors = array( - JSON_ERROR_NONE => null, + JSON_ERROR_NONE => false, JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', @@ -1322,8 +1338,21 @@ protected function jsonLastErrorMsg($file) { ); $error = json_last_error(); $errorMessage = array_key_exists($error, $errors) ? $errors[$error] : "Unknown error ({$error})"; - if ($errorMessage != null) { - print "The JSON file, ".$file.", wasn't loaded. The error: ".$errorMessage."\n"; + return $errorMessage; + } + + /** + * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 + * @param {String} the file that generated the error + */ + protected function jsonLastErrorMsg($file,$message,$data) { + print "\nThe JSON file, ".$file.", wasn't loaded. The error: ".$message."\n"; + if ($message == "Syntax error, malformed JSON") { + print "\n"; + $parser = new \Seld\JsonLint\JsonParser(); + $error = $parser->lint($data); + print $error->getMessage(); + print "\n\n"; } } From 3f68c7a426885561c49bed750032c806053f4b9e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 08:55:30 -0400 Subject: [PATCH 021/166] registering jsonlint as a class --- core/builder.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/builder.php b/core/builder.php index 925adf98e..22915f24c 100644 --- a/core/builder.php +++ b/core/builder.php @@ -28,6 +28,8 @@ $loader->setNamespaceSeparator("_"); $loader->register(); +$loader = new SplClassLoader('Seld', __DIR__.'/lib'); +$loader->register(); /******************************* * Console Set-up From d134683794a763cac163e09af0485d1a5b9814d3 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 10:54:05 -0400 Subject: [PATCH 022/166] removing an old $this->mpl call --- core/lib/PatternLab/Watcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index 03ead7e40..491a8922b 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -216,7 +216,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals $c = true; // taking out the garbage. basically killing mustache after each run. - unset($this->mpl); + unset($this->pl); unset($this->msf); unset($this->mv); if (gc_enabled()) gc_collect_cycles(); From 5315038f5fb5caa45361475f1b1f2c5465d9ff1a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 14:04:28 -0400 Subject: [PATCH 023/166] copy fix --- core/lib/PatternLab/PatternLoader.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index c02a221c5..80b11a3da 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -158,18 +158,18 @@ public function findReplaceParameters($fileData, $parameters) { foreach ($v as $v2) { $partialData .= $this->findReplaceParameters($matches[2], $v2); } - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); } } } else if ($v == "true") { - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else if ($v == "false") { - $fileData = preg_replace('/{{\^([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\#([\s]*'.$k .'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else { - $fileData = preg_replace('/{{{([\s]*'.$k .'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} - $fileData = preg_replace('/{{([\s]*'.$k .'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} + $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} + $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} } } return $fileData; From 0b544b250806c8fa9e4b51c4715e9f3fc121a82e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 14:04:54 -0400 Subject: [PATCH 024/166] can now swap listItems via pattern parameters --- core/lib/PatternLab/PatternLoader.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index 80b11a3da..6cf5dfa8d 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -167,6 +167,9 @@ public function findReplaceParameters($fileData, $parameters) { } else if ($v == "false") { $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + } else if ($k == "listItems") { + $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); + $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); } else { $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} From 51595ca76a7d602a3331ed09c901f2db7c45f33d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 14:10:47 -0400 Subject: [PATCH 025/166] adding in a missing comment --- core/lib/PatternLab/Builder.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 1c86de92a..7a0004a76 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -1111,6 +1111,11 @@ protected function getPatternName($pattern, $clean = true) { return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; } + /** + * Sets the pattern state on other patterns based on the pattern state for a given partial + * @param {String} the pattern state + * @param {String} the pattern partial + */ protected function setPatternState($patternState, $patternPartial) { // set-up some defaults From 1535aa8b85c0e5088a7ae81d5078ef887693b4ca Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 14:15:14 -0400 Subject: [PATCH 026/166] making the help command more explicit when using the console --- core/builder.php | 3 +++ core/lib/PatternLab/Console.php | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/builder.php b/core/builder.php index 22915f24c..670ece303 100644 --- a/core/builder.php +++ b/core/builder.php @@ -55,6 +55,9 @@ // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); +// set-up the help command +$console->setCommand("h","help","Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options."); + /******************************* * Figure out what to run diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index def64cdce..8572be8c8 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -15,8 +15,8 @@ class Console { - private $optionsShort = "h"; - private $optionsLong = array("help"); + private $optionsShort = ""; + private $optionsLong = array(); private $options = array(); private $commands = array(); private $self; From 5d490913054e7119b8f97eb1108e82df9102308b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Sun, 20 Apr 2014 14:47:05 -0400 Subject: [PATCH 027/166] a little smarter listItem replacement --- core/lib/PatternLab/PatternLoader.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternLoader.php index 6cf5dfa8d..344b04002 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternLoader.php @@ -150,6 +150,7 @@ public function getPartialInfo($partial) { * @return {String} the modified file contents */ public function findReplaceParameters($fileData, $parameters) { + $numbers = array("zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); foreach ($parameters as $k => $v) { if (is_array($v)) { if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { @@ -168,8 +169,11 @@ public function findReplaceParameters($fileData, $parameters) { $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} } else if ($k == "listItems") { - $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); - $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); + $v = ((int)$v != 0) && ((int)$v < 13) ? $numbers[$v] : $v; + if (($v != "zero") && in_array($v,$numbers)) { + $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); + $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); + } } else { $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} From 327eec098a3b7faf438f46c4e1f5e8382caa995a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Wed, 30 Apr 2014 16:35:54 -0400 Subject: [PATCH 028/166] updating the name of the lib --- core/builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/builder.php b/core/builder.php index 670ece303..236e4f1b2 100644 --- a/core/builder.php +++ b/core/builder.php @@ -28,7 +28,7 @@ $loader->setNamespaceSeparator("_"); $loader->register(); -$loader = new SplClassLoader('Seld', __DIR__.'/lib'); +$loader = new SplClassLoader('Seld\JsonLint', __DIR__.'/lib'); $loader->register(); /******************************* From 501ade8b3ec91aef4b0294d46972dbfada3c1e6a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Wed, 30 Apr 2014 16:36:39 -0400 Subject: [PATCH 029/166] removing unnecessary styleguide files --- core/styleguide/html/styleguide.html | 2682 -------------------------- core/templates/styleguide.mustache | 47 - 2 files changed, 2729 deletions(-) delete mode 100644 core/styleguide/html/styleguide.html delete mode 100644 core/templates/styleguide.mustache diff --git a/core/styleguide/html/styleguide.html b/core/styleguide/html/styleguide.html deleted file mode 100644 index c62f30565..000000000 --- a/core/styleguide/html/styleguide.html +++ /dev/null @@ -1,2682 +0,0 @@ - - - - Pattern Lab - - - - - - - - - - - - - - - - - - - - - - - -
- - -
-
-

Colors

-
-
    -
  • - - #ff0000 -
  • -
  • - - #00ff00 -
  • -
  • - - #0000ff -
  • -
  • - - #ffff00 -
  • -
  • - - #00ffff -
  • -
  • - - #ff00ff -
  • -
  • - - #ffffff -
  • -
  • - - #808080 -
  • -
  • - - #000000 -
  • -
- - - -
-
-
-

Fonts

-
-

Primary font: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

-

Primary font italic: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

-

Primary font bold: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

-

Secondary font: Georgia, Times, "Times New Roman", serif;

-

Secondary font italic: Georgia, Times, "Times New Roman", serif;

-

Secondary font bold; Georgia, Times, "Times New Roman", serif;

- -
-
-
-

Animations

-
-
Fade: Duration: 0.3s Easing: ease-out (Hover to see effect)
- -
Movement: Duration: 0.8s Easing: ease-in-out; (Hover to see effect)
- -
-
-
-

Visibility

-
-

This text is hidden on smaller screens

- -

This text is only visible on smaller screens

- -

This text is hidden on medium screens only

- -

This text is only visible on medium screens

- -

This text is hidden on large screens

- -

This text is only visible on large screens

- -
-
-
-

Headings

-
-

Heading Level 1

-

Heading Level 2

-

Heading Level 3

-

Heading Level 4

-
Heading Level 5
-
Heading Level 6
- -
-
-
-

Subheadings

-
-

Subheading Level 1

-

Subheading Level 2

-

Subheading Level 3

-

Subheading Level 4

-
Subheading Level 5
-
Subheading Level 6
- -
-
- -
-

Paragraph

-
-

So, setting about it as methodically as men might smoke out a wasps' nest, the Martians spread this strange stifling vapour over the Londonward country. The horns of the crescent slowly moved apart, until at last they formed a line from Hanwell to Coombe and Malden. All night through their destructive tubes advanced.

- -
-
-
-

Blockquote

-
-
-

A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

-
- -
-
-
-

Inline Elements

-
-
-

This is a text link

- -

Strong is used to indicate strong importance

- -

This text has added emphasis

- -

The b element is stylistically different text from normal text, without any special importance

- -

The i element is text that is set off from the normal text

- -

The u element is text with an unarticulated, though explicitly rendered, non-textual annotation

- -

This text is deleted and This text is inserted

- -

This text has a strikethrough

- -

Superscript®

- -

Subscript for things like H2O

- -

This small text is small for for fine print, etc.

- -

Abbreviation: HTML

- -

Keybord input: Cmd

- -

This text is a short inline quotation

- -

This is a citation - -

The dfn element indicates a definition.

- -

The mark element indicates a highlight

- -

This is what inline code looks like.

- -

This is sample output from a computer program

- -

The variarble element, such as x = y

-
- -
-
-
-

Time

-
- - -
-
-
-

Preformatted Text

-
-
  	
-P R E F O R M A T T E D T E X T
-! " # $ % & ' ( ) * + , - . /
-0 1 2 3 4 5 6 7 8 9 : ; < = > ?
-@ A B C D E F G H I J K L M N O
-P Q R S T U V W X Y Z [ \ ] ^ _
-` a b c d e f g h i j k l m n o
-p q r s t u v w x y z { | } ~ 
-
- -
-
-
-

Emphasis Colors

-
-

This is what error text looks like

-

This is what valid text looks like

-

This is what warning text looks like

-

This is what information text looks like.

- -
-
-
-

Hr

-
-
- -
-
-
-

Caption

-
-

A caption can be applied to an image, quote, form field, etc.

- -
-
-
-

Unordered

-
-
-
    -
  • This is a list item in an unordered list
  • -
  • An unordered list is a list in which the sequence of items is not important. Sometimes, an unordered list is a bulleted list. And this is a long list item in an unordered list that can wrap onto a new line.
  • -
  • - Lists can be nested inside of each other -
      -
    • This is a nested list item
    • -
    • This is another nested list item in an unordered list
    • -
    -
  • -
  • This is the last list item
  • -
-
- -
-
-
-

Ordered

-
-
-
    -
  1. This is a list item in an ordered list
  2. -
  3. An ordered list is a list in which the sequence of items is important. An ordered list does not necessarily contain sequence characters.
  4. -
  5. - Lists can be nested inside of each other -
      -
    1. This is a nested list item
    2. -
    3. This is another nested list item in an ordered list
    4. -
    -
  6. -
  7. This is the last list item
  8. -
-
- -
-
-
-

Definition

-
-
-
Definition List
-
A number of connected items or names written or printed consecutively, typically one below the other.
-
This is a term.
-
This is the definition of that term, which both live in a dl.
-
Here is another term.
-
And it gets a definition too, which is this line.
-
Here is term that shares a definition with the term below.
-
And it gets a definition too, which is this line.
-
- -
-
- -
-

Landscape 4x3

-
- 4x3 Image - -
-
-
-

Landscape 16x9

-
- 16x9 Image - -
-
-
-

Square

-
- Square Thumbnail - -
-
-
-

Avatar

-
- Avatar - -
-
-
-

Icons

-
-
    -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • - -
  • -
- -
-
-
-

Loading Icon

-
- Loading - -
-
-
-

Favicon

-
- - - -
-
-
-

Text Fields

-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
-

Select Menu

-
-
-
- - -
-
- -
-
-
-

Checkbox

-
-
-
- Checkbox * -
    -
  • -
  • -
  • -
-
-
- -
-
-
-

Radio Buttons

-
-
-
- Radio -
    -
  • -
  • -
  • -
-
-
- -
-
-
-

Html5 Inputs

-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-

Buttons

-
-

Button

-

Alternate Button

-

Disabled Button

-

Text Button

- -
-
-
-

Table

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Table Heading 1Table Heading 2Table Heading 3Table Heading 4Table Heading 5
Table Footer 1Table Footer 2Table Footer 3Table Footer 4Table Footer 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
- -
-
-
-

Byline Author Only

-
- -
    -
  • - - #ff0000 -
  • -
  • - - #00ff00 -
  • -
  • - - #0000ff -
  • -
  • - - #ffff00 -
  • -
  • - - #00ffff -
  • -
  • - - #ff00ff -
  • -
  • - - #ffffff -
  • -
  • - - #808080 -
  • -
  • - - #000000 -
  • -
- - - -
-
-
-

Byline Author Time

-
- - -
-
-
-

Address

-
-
-
Company Name
-
-
1234 Main St.
- Anywhere, - 101010, - CA -
U.S.A
-
-
+1.888.123.4567
-
- -
-
-
-

Heading Group

-
-
-

This is the heading group's main heading

-

This is the heading group's subheading

-
- -
-
-
-

Blockquote With Citation

-
-
-

A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

- Quote Source -
- -
-
-
-

Intro Text

-
-

The intro text may be a lead-in to the passage of text, or it may just be used to create a visual distinction between the rest of the passage of text.

- -
-
-
-

Pullquote

-
-
-

A pull quote is a quotation or excerpt from an article

-
- -
-
-
-

One Up

-
-
- -
-
1/1
-
- -
- -
-
-
-

Two Up

-
-
- -
-
1/2
-
1/2
-
-
- -
-
-
-

Three Up

-
-
- -
-
1/3
-
1/3
-
1/3
-
-
- -
-
-
-

Four Up

-
-
- -
-
1/4
-
1/4
-
1/4
-
1/4
-
-
- -
-
-
-

Media Block

-
- - -
-
-
-

Headline Byline

-
- - -
-
-
-

Block Hero

-
- - -
-
-
-

Block Thumb Headline

-
- - -
-
-
-

Headline Only

-
- - -
-
-
-

Inset Block

-
- - -
-
-
-

Figure With Caption

-
-
- 4x3 Image
This is an example of an image with a caption. Photo captions, also known as cutlines, are a few lines of text used to explain or elaborate on published photographs.
-
- -
-
- -
-

Comment Form

-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- -
-
-
-

Newsletter

-
- - -
-
-
-

Primary Nav

-
- - - -
-
- -
-

Breadcrumbs

-
- - -
-
-
-

Pagination

-
-
    -
  1. 1
  2. -
  3. 2
  4. -
  5. 3
  6. -
  7. 4
  8. -
  9. 5
  10. -
  11. 6
  12. -
  13. 7
  14. -
- -
-
-
-

Tabs

-
-
- -
- -
-
-
-

Social Share

-
- - -
-
-
-

Accordion

-
-
- -
- -
-
-
-

Single Comment

-
-
  • -
    - Avatar

    Lacy Way

    -
    -
    -

    So, setting about it as methodically as men might smoke out a wasps' nest, the Martians spread this strange stifling vapour over the Londonward country. The horns of the crescent slowly moved apart, until at last they formed a line from Hanwell to Coombe and Malden. All night through their destructive tubes advanced.

    -
    -
  • - -
    -
    -
    -

    Header

    -
    - - - - -
    -
    - -
    -

    Article Body

    -
    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer fringilla sem a urna porttitor fringilla. Nulla eget justo felis.

    - -

    Aliquam erat volutpat. Mauris vulputate scelerisque feugiat. Cras a erat a diam venenatis aliquam. Sed tempus, purus ac pretium varius, risus orci sagittis purus, quis auctor libero magna nec magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas eros dolor, rutrum eu sollicitudin eu, commodo at leo. Suspendisse potenti. Sed eu nibh sit amet quam auctor feugiat vel et risus. Maecenas eu urna adipiscing neque dictum mollis suscipit in augue. Praesent pulvinar condimentum sagittis. Maecenas laoreet neque non eros consectetur fringilla. Donec vitae risus leo, vitae pharetra ipsum. Sed placerat eros eget elit iaculis semper. Aliquam congue blandit orci ac pretium.

    - -16x9 Image -

    Aliquam ultrices cursus mauris, eget volutpat justo mattis nec. Sed a orci turpis. Aliquam aliquet placerat dui, consectetur tincidunt leo tristique et. Vivamus enim nisi, blandit a venenatis quis, convallis et arcu. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris libero sapien, placerat in sodales eu, tempor quis dui. Vivamus egestas faucibus pulvinar. Maecenas eget diam nunc. Phasellus at sem eros, ac suscipit neque. Phasellus sollicitudin libero a odio dignissim scelerisque. Aliquam purus nulla, tempor eget ullamcorper quis, rhoncus non dui. -

    - -
    -

    A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

    -
    -

    Cras at fringilla ipsum. Donec nec libero eget est blandit dignissim a eu ante. Morbi augue nulla, luctus eu sagittis vel, malesuada ut felis. Aliquam erat volutpat. Morbi malesuada augue ac massa hendrerit fermentum. Integer scelerisque lacus a dolor convallis lobortis. Curabitur mollis ante in massa ultricies dignissim. -

    - -
    -
      -
    • This is a list item in an unordered list
    • -
    • An unordered list is a list in which the sequence of items is not important. Sometimes, an unordered list is a bulleted list. And this is a long list item in an unordered list that can wrap onto a new line.
    • -
    • - Lists can be nested inside of each other -
        -
      • This is a nested list item
      • -
      • This is another nested list item in an unordered list
      • -
      -
    • -
    • This is the last list item
    • -
    -
    -
    -
      -
    1. This is a list item in an ordered list
    2. -
    3. An ordered list is a list in which the sequence of items is important. An ordered list does not necessarily contain sequence characters.
    4. -
    5. - Lists can be nested inside of each other -
        -
      1. This is a nested list item
      2. -
      3. This is another nested list item in an ordered list
      4. -
      -
    6. -
    7. This is the last list item
    8. -
    -
    -

    Donec posuere fringilla nunc, vitae venenatis diam scelerisque vel. Nullam vitae mauris magna. Mauris et diam quis justo volutpat tincidunt congue nec magna. Curabitur vitae orci elit. Ut mollis massa id magna vestibulum consequat. Proin rutrum lectus justo, sit amet tincidunt est. Vivamus vitae lacinia risus. -

    - -
    -

    A pull quote is a quotation or excerpt from an article

    -
    -

    Donec venenatis imperdiet tortor, vitae blandit odio interdum ut. Integer orci metus, lobortis id lacinia eget, rutrum vitae justo. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In pretium fermentum justo nec pharetra. Maecenas eget dapibus justo. Ut quis est risus. Nullam et eros at odio commodo venenatis quis et augue. Sed sed augue at tortor porttitor hendrerit nec ut nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Proin sollicitudin enim consectetur mi commodo quis cursus ante pretium. Nunc gravida cursus nisi in gravida. Suspendisse eget tortor sed urna consequat tincidunt. Etiam eget convallis lectus. Suspendisse cursus rutrum massa ac faucibus. -

    -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis, suscipit repellendus nulla accusantium deserunt sed explicabo voluptate sapiente ratione inventore molestiae nihil earum repellat quia odit vitae perspiciatis aliquam amet?

    - -
    -
    -
    -

    Comment Thread

    -
    -
    -
    -

    59 Comments

    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
      -
    • - -
      -

      Mung bean squash sorrel taro coriander collard greens gumbo bitterleaf tomato. Taro water chestnut celtuce turnip yarrow celery endive scallion black-eyed pea onion. Aubergine dulse turnip greens mustard salsify garlic soybean parsley bitterleaf desert raisin courgette.

      -
      -
    • - -
      -

      Boudin sausage jerky pastrami ground round salami biltong. Sausage fatback strip steak doner pork loin, pork belly drumstick ham short loin hamburger shankle. Short ribs sirloin rump tri-tip beef biltong. Meatball pig salami, jowl pork loin fatback short loin drumstick andouille.

      -
      -
    • - -
      -

      Like button audience atomization overcome Colbert bump Free Darko inverted pyramid we will make them pay, digital circulation strategy Like button totally blowing up on Twitter church of the savvy. Pictures of Goats section open source discuss Frontline analog thinking filters paidContent.

      -
      -
    • - -
      -

      Sugar plum wafer soufflé ice cream. Wafer topping biscuit pie gummi bears topping. Gummies toffee powder applicake oat cake cookie. Bear claw candy tootsie roll fruitcake danish applicake candy canes macaroon. Liquorice tiramisu danish cotton candy gummies. Tiramisu dessert gummi bears macaroon sweet roll jelly-o gummi bears marzipan.

      -
      -
    • - -
      -

      Fanny pack ullamco et veniam semiotics. Shoreditch PBR reprehenderit cliche, magna Tonx aesthetic. Narwhal photo booth DIY aute post-ironic anim. Vice cliche brunch est before they sold out fap, street art Odd Future fashion axe messenger bag nihil Tonx tattooed. Nihil hashtag incididunt, do eu art party Banksy jean shorts four loko typewriter.

      -
      -
    -
    -
      -
    1. 1
    2. -
    3. 2
    4. -
    5. 3
    6. -
    7. 4
    8. -
    9. 5
    10. -
    11. 6
    12. -
    13. 7
    14. -
    - -
    -
    - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/templates/styleguide.mustache b/core/templates/styleguide.mustache deleted file mode 100644 index 88fca2338..000000000 --- a/core/templates/styleguide.mustache +++ /dev/null @@ -1,47 +0,0 @@ - - - - - From 98e4cbfb15005f27f32b66902a753d0ea1c12454 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Wed, 30 Apr 2014 16:41:47 -0400 Subject: [PATCH 030/166] adding partials for the viewer pop-ups --- core/templates/index.mustache | 38 +------------------ .../partials/viewerAnnotation.mustache | 4 ++ core/templates/partials/viewerCode.mustache | 32 ++++++++++++++++ 3 files changed, 38 insertions(+), 36 deletions(-) create mode 100644 core/templates/partials/viewerAnnotation.mustache create mode 100644 core/templates/partials/viewerCode.mustache diff --git a/core/templates/index.mustache b/core/templates/index.mustache index dfccf6a29..6476ce337 100644 --- a/core/templates/index.mustache +++ b/core/templates/index.mustache @@ -44,46 +44,12 @@ diff --git a/core/templates/partials/viewerAnnotation.mustache b/core/templates/partials/viewerAnnotation.mustache new file mode 100644 index 000000000..2f215f8eb --- /dev/null +++ b/core/templates/partials/viewerAnnotation.mustache @@ -0,0 +1,4 @@ + +
    \ No newline at end of file diff --git a/core/templates/partials/viewerCode.mustache b/core/templates/partials/viewerCode.mustache new file mode 100644 index 000000000..8d2273b47 --- /dev/null +++ b/core/templates/partials/viewerCode.mustache @@ -0,0 +1,32 @@ +
    + Close +
    +
    +
    + Loading pattern... +
    + + + +
    +
      +
    • HTML
    • +
    • Mustache
    • + +
    +
    +
    +
    +
    \ No newline at end of file From b61c49d36cd6a7b526cd9a65209dbe3db82a1a2b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Wed, 30 Apr 2014 16:57:41 -0400 Subject: [PATCH 031/166] smarter sorting of MQs --- core/lib/PatternLab/Builder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 7a0004a76..0680ac611 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -503,7 +503,8 @@ protected function gatherMQs() { } } - sort($mqs); + usort($mqs, "strnatcmp"); + return $mqs; } From 14adfa64ad7e5d308f7fb385fd2a451ab10fe015 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Thu, 1 May 2014 10:29:18 -0400 Subject: [PATCH 032/166] removing refs to PHP in core/templates --- core/templates/pattern-header-footer/footer-pattern.html | 2 +- core/templates/viewall.mustache | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/templates/pattern-header-footer/footer-pattern.html b/core/templates/pattern-header-footer/footer-pattern.html index 86143b407..4ad384a8c 100644 --- a/core/templates/pattern-header-footer/footer-pattern.html +++ b/core/templates/pattern-header-footer/footer-pattern.html @@ -1,6 +1,6 @@ From 2bf6ec84981ef4e7920353e6566b64a5eeaaa462 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Thu, 1 May 2014 11:07:57 -0400 Subject: [PATCH 033/166] handle underscores for lineage --- core/lib/PatternLab/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 0680ac611..7c9ed5bf3 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -1003,7 +1003,7 @@ protected function gatherPatternInfo() { protected function getLineage($filename) { $data = file_get_contents(__DIR__.$this->sp.$filename.".".$this->patternExtension); //$data = file_get_contents($filename); - if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)}}/',$data,$matches)) { + if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-_]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)}}/',$data,$matches)) { return $matches[2]; } return array(); From 1389f7dc4952b7bd4a76a7792300f7fe0f371854 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:00:47 -0400 Subject: [PATCH 034/166] adding initial support for snapshot tool --- core/builder.php | 11 ++ core/lib/PatternLab/Snapshot.php | 127 ++++++++++++++++++ .../lib/PatternLab/SnapshotFilterIterator.php | 26 ++++ core/templates/index.mustache | 2 +- core/templates/partials/ishControls.mustache | 1 + 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 core/lib/PatternLab/Snapshot.php create mode 100644 core/lib/PatternLab/SnapshotFilterIterator.php diff --git a/core/builder.php b/core/builder.php index 236e4f1b2..4db453576 100644 --- a/core/builder.php +++ b/core/builder.php @@ -52,6 +52,10 @@ $console->setCommandOption("w","n","nocache","Set the cacheBuster value to 0.","To turn off the cacheBuster:"); $console->setCommandOption("w","r","autoreload","Turn on the auto-reload service.","To turn on auto-reload:"); +// set-up the snapshot command and options +$console->setCommand("s","snapshot","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); +$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); + // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); @@ -84,6 +88,7 @@ $moveStatic = ($console->findCommandOption("p|patternsonly")) ? false : true; $noCacheBuster = ($console->findCommandOption("n|nocache")) ? true : false; $autoReload = ($console->findCommandOption("r|autoreload")) ? true : false; + $snapshotDir = $console->findCommandOption("d|dir"); if (($command == "g") || ($command == "b")) { @@ -105,6 +110,12 @@ $w = new PatternLab\Watcher($config); $w->watch($autoReload,$moveStatic,$noCacheBuster); + } else if ($command == "s") { + + // run the snapshot command + $s = new PatternLab\Snapshot($config); + $s->takeSnapshot($snapshotDir); + } else if ($command == "v") { // write out the version number diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php new file mode 100644 index 000000000..9a52ca50d --- /dev/null +++ b/core/lib/PatternLab/Snapshot.php @@ -0,0 +1,127 @@ +publicDir = __DIR__."/../../../".$config["publicDir"]; + $this->snapshotsBase = $this->publicDir."/snapshots/"; + } + + /** + * Get the arguments that have been passed to the script via the commmand line + */ + public function takeSnapshot($dir) { + + $snapshotsDir = ""; + + // check to see if snapshots exists, if it doesn't make it + if (!is_dir($this->snapshotsBase)) { + mkdir($this->snapshotsBase); + } + + // see if the dir passed through exists. if it does highlight an error. + if ($dir) { + + // check to see if the given directory exists + if (is_dir($this->snapshotsBase.$dir)) { + print "The directory, ".$dir.", already exists. Please choose a new one for your snapshot.\n"; + exit; + } + + // set-up the final snapshot directory + $snapshotsDir = $this->snapshotsBase.$dir; + + } else { + + // get a list of dirs + $scannedDirs = scandir($this->snapshotsBase); + + // remove the dot files + $key = array_search('.', $scannedDirs); + unset($scannedDirs[$key]); + $key = array_search('..', $scannedDirs); + unset($scannedDirs[$key]); + + // set-up the final snapshot directory + $dirCount = 0; + foreach ($scannedDirs as $scannedDir) { + if (preg_match("/^v[0-9]{1,3}$/",$scannedDir)) { + $dirCount++; + } + } + + $dirCount = $dirCount + 1; + $snapshotsDir = $this->snapshotsBase."v".$dirCount; + + } + + // make the snapshot directory + mkdir($snapshotsDir); + + // iterate over all of the other files in the source directory + $directoryIterator = new \RecursiveDirectoryIterator($this->publicDir); + $filteredIterator = new SnapshotFilterIterator($directoryIterator); + $objects = new \RecursiveIteratorIterator($filteredIterator, \RecursiveIteratorIterator::SELF_FIRST); + + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); + + foreach($objects as $name => $object) { + + // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored + $fileName = str_replace($this->publicDir.DIRECTORY_SEPARATOR,"",$name); + + // check to see if it's a new directory + if ($object->isDir()) { + mkdir($snapshotsDir."/".$fileName); + } + + // check to see if it's a new file or a file that has changed + if ($object->isFile()) { + copy($this->publicDir."/".$fileName,$snapshotsDir."/".$fileName); + } + + } + + // re-scan to get the latest addition + $html = ""; + $scannedDirs = scandir($this->snapshotsBase); + + // remove the dot files + $key = array_search('.', $scannedDirs); + unset($scannedDirs[$key]); + $key = array_search('..', $scannedDirs); + unset($scannedDirs[$key]); + $key = array_search('index.html', $scannedDirs); + unset($scannedDirs[$key]); + + foreach ($scannedDirs as $scanDir) { + $html .= "
  • ".$scanDir."
  • \n"; + } + + $templateLoader = new TemplateLoader(); + $m = $templateLoader->fileSystem(); + $r = $m->render('snapshot', array("html" => $html)); + + file_put_contents($this->snapshotsBase."index.html",$r); + + print "finished taking a snapshot...\n"; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/SnapshotFilterIterator.php b/core/lib/PatternLab/SnapshotFilterIterator.php new file mode 100644 index 000000000..2c6262e4a --- /dev/null +++ b/core/lib/PatternLab/SnapshotFilterIterator.php @@ -0,0 +1,26 @@ +current()->getFilename(),self::$FILTERS,true); + } + +} \ No newline at end of file diff --git a/core/templates/index.mustache b/core/templates/index.mustache index 6476ce337..e758d81fa 100644 --- a/core/templates/index.mustache +++ b/core/templates/index.mustache @@ -34,7 +34,7 @@
    - +
    diff --git a/core/templates/partials/ishControls.mustache b/core/templates/partials/ishControls.mustache index 4a9ae5cb7..83dc12cb6 100644 --- a/core/templates/partials/ishControls.mustache +++ b/core/templates/partials/ishControls.mustache @@ -53,6 +53,7 @@
      {{^ ishControlsHide.tools-follow }}
    • Page Follow
    • {{/ ishControlsHide.tools-follow }} {{^ ishControlsHide.tools-reload }}
    • Auto-reload
    • {{/ ishControlsHide.tools-reload }} + {{^ ishControlsHide.tools-snapshot }}
    • Snapshots
    • {{/ ishControlsHide.tools-snapshot }} {{^ ishControlsHide.tools-shortcuts }}
    • Keyboard Shortcuts{{/ ishControlsHide.tools-shortcuts }} {{^ ishControlsHide.tools-docs }}
    • Pattern Lab Docs{{/ ishControlsHide.tools-docs }}
    From 54f1186041b9e6342b4e3f0385e488cd3b497ca9 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:01:21 -0400 Subject: [PATCH 035/166] better support for snapshots --- core/lib/PatternLab/Builder.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 7c9ed5bf3..226ac41fc 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -52,6 +52,12 @@ public function __construct($config = array()) { $value = "tools-reload"; $this->$key->$value = true; } + $toolssnapshot = "tools-snapshot"; // i was an idgit and used dashes + if (!isset($this->$key->$toolssnapshot)) { + if (!is_dir($this->pd."/snapshots")) { + $this->$key->$toolssnapshot = true; + } + } } else { $this->$key = $value; } @@ -1227,7 +1233,7 @@ protected function cleanPublic() { $publicDirs = glob($this->pd."/*",GLOB_ONLYDIR); // make sure some directories aren't deleted - $ignoreDirs = array("styleguide"); + $ignoreDirs = array("styleguide","snapshots"); foreach ($ignoreDirs as $ignoreDir) { $key = array_search($this->pd."/".$ignoreDir,$publicDirs); if ($key !== false){ From cb8ee08945fb7f9d1cfd2eeb92537a69ce732130 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:02:00 -0400 Subject: [PATCH 036/166] adding a template loader class --- core/lib/PatternLab/Builder.php | 36 ++++++------------------ core/lib/PatternLab/TemplateLoader.php | 38 ++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 core/lib/PatternLab/TemplateLoader.php diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 226ac41fc..0312b15d0 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -79,29 +79,6 @@ public function __construct($config = array()) { } - - - /** - * Load a new Mustache instance that uses the File System Loader - * - * @return {Object} an instance of the Mustache engine - */ - protected function loadMustacheFileSystemLoaderInstance() { - $this->mfs = new \Mustache_Engine(array( - "loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/"), - "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/partials/") - )); - } - - /** - * Load a new Mustache instance that uses the File System Loader - * - * @return {Object} an instance of the Mustache engine - */ - protected function loadMustacheVanillaInstance() { - $this->mv = new \Mustache_Engine; - } - /** * Renders a given pattern file using Mustache and incorporating the provided data * @param {String} the filename of the file to be rendered @@ -156,8 +133,9 @@ protected function renderPattern($f,$p) { protected function generateMainPages() { // make sure $this->mfs & $this->mv are refreshed - $this->loadMustacheFileSystemLoaderInstance(); - $this->loadMustacheVanillaInstance(); + $templateLoader = new TemplateLoader(); + $this->mfs = $templateLoader->fileSystem(); + $this->mv = $templateLoader->vanilla(); // get the source pattern paths $patternPathDests = array(); @@ -232,9 +210,11 @@ protected function generatePatterns() { $this->addPatternHF = true; // make sure $this->pl & $this->mv are refreshed - $patternLoader = new PatternLoader($this->patternPaths); - $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); - $this->loadMustacheVanillaInstance(); + $patternLoader = new PatternLoader($this->patternPaths); + $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); + + $templateLoader = new TemplateLoader(); + $this->mv = $templateLoader->vanilla(); // loop over the pattern paths to generate patterns for each foreach($this->patternPaths as $patternType) { diff --git a/core/lib/PatternLab/TemplateLoader.php b/core/lib/PatternLab/TemplateLoader.php new file mode 100644 index 000000000..72b62a3e2 --- /dev/null +++ b/core/lib/PatternLab/TemplateLoader.php @@ -0,0 +1,38 @@ + new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/"), + "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/partials/") + )); + } + + /** + * Load a new Mustache instance that is just a vanilla Mustache rendering engine + * + * @return {Object} an instance of the Mustache engine + */ + public static function vanilla() { + return new \Mustache_Engine; + } + +} \ No newline at end of file From b5799726fa7c557ed5417873f657606156bf9f8e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:04:10 -0400 Subject: [PATCH 037/166] added support for a publicDir and sourceDir config option --- core/config/config.ini.default | 8 +++++++- core/lib/PatternLab/Builder.php | 17 +++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/core/config/config.ini.default b/core/config/config.ini.default index 25fa0ec0c..5ef75ef2d 100644 --- a/core/config/config.ini.default +++ b/core/config/config.ini.default @@ -43,4 +43,10 @@ styleGuideExcludes = "" cacheBusterOn = "true" // the pattern rending engine -patternEngine = "mustache" \ No newline at end of file +patternEngine = "mustache" + +// the public directory +publicDir = "public" + +// the source directory +sourceDir = "source" \ No newline at end of file diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 0312b15d0..946b9adfd 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -26,6 +26,12 @@ public function __construct($config = array()) { exit; } + // set-up the source & public dirs + $this->sp = "/../../../".$config["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; + $this->pp = "/../../../".$config["publicDir"]."/patterns".DIRECTORY_SEPARATOR; + $this->sd = __DIR__."/../../../".$config["sourceDir"]; + $this->pd = __DIR__."/../../../".$config["publicDir"]; + // populate some standard variables out of the config foreach ($config as $key => $value) { @@ -64,12 +70,6 @@ public function __construct($config = array()) { } - // set-up the source & public dirs - $this->sp = "/../../../source/_patterns".DIRECTORY_SEPARATOR; - $this->pp = "/../../../public/patterns".DIRECTORY_SEPARATOR; - $this->sd = __DIR__."/../../../source"; - $this->pd = __DIR__."/../../../public"; - // provide the default for enable CSS. performance hog so it should be run infrequently $this->enableCSS = false; $this->patternCSS = array(); @@ -281,8 +281,9 @@ private function generatePatternFile($f,$p,$path,$state) { protected function generateViewAllPages() { // make sure $this->mfs & $this->mv are refreshed on each generation of view all - $this->loadMustacheFileSystemLoaderInstance(); - $this->loadMustacheVanillaInstance(); + $templateLoader = new TemplateLoader(); + $this->mfs = $templateLoader->fileSystem(); + $this->mv = $templateLoader->vanilla(); // add view all to each list $i = 0; $k = 0; From b5edbae4aa15707a831f09099cd13b794126f237 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:04:32 -0400 Subject: [PATCH 038/166] simplifying console options --- core/builder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/builder.php b/core/builder.php index 4db453576..c157b931e 100644 --- a/core/builder.php +++ b/core/builder.php @@ -84,10 +84,10 @@ $config = $configurer->getConfig(); // set-up required vars - $enableCSS = ($console->findCommandOption("c|enablecss")) ? true : false; + $enableCSS = $console->findCommandOption("c|enablecss"); $moveStatic = ($console->findCommandOption("p|patternsonly")) ? false : true; - $noCacheBuster = ($console->findCommandOption("n|nocache")) ? true : false; - $autoReload = ($console->findCommandOption("r|autoreload")) ? true : false; + $noCacheBuster = $console->findCommandOption("n|nocache"); + $autoReload = $console->findCommandOption("r|autoreload"); $snapshotDir = $console->findCommandOption("d|dir"); if (($command == "g") || ($command == "b")) { From 2c4f09cf155da595665dbd0285fa394743159859 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 10:04:46 -0400 Subject: [PATCH 039/166] return passed values for console options --- core/lib/PatternLab/Console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index 8572be8c8..3a39a61f5 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -92,7 +92,7 @@ public function findCommandOption($args) { $args = explode("|",$args); foreach ($args as $arg) { if (isset($this->options[$arg])) { - return true; + return empty($this->options[$arg]) ? true : $this->options[$arg]; } } return false; From e6e2ab38bc5b542749c5108a8b7201395c835c4d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:48:56 -0400 Subject: [PATCH 040/166] template helper to centralize header/footer creation --- core/lib/PatternLab/TemplateHelper.php | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 core/lib/PatternLab/TemplateHelper.php diff --git a/core/lib/PatternLab/TemplateHelper.php b/core/lib/PatternLab/TemplateHelper.php new file mode 100644 index 000000000..827d7a4ad --- /dev/null +++ b/core/lib/PatternLab/TemplateHelper.php @@ -0,0 +1,46 @@ +patternHead = str_replace("{% pattern-lab-head %}",$htmlHead,$patternHead); + $this->patternFoot = str_replace("{% pattern-lab-foot %}",$extraFoot.$htmlFoot,$patternFoot); + $this->mainPageHead = $this->patternHead; + $this->mainPageFoot = str_replace("{% pattern-lab-foot %}",$htmlFoot,$patternFoot); + + } + +} \ No newline at end of file From 8d9ace21a62b25a8643caafabcd10313ea6662f2 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:49:30 -0400 Subject: [PATCH 041/166] adding system header footer --- core/lib/PatternLab/Snapshot.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php index 9a52ca50d..f16d21f42 100644 --- a/core/lib/PatternLab/Snapshot.php +++ b/core/lib/PatternLab/Snapshot.php @@ -19,6 +19,7 @@ class Snapshot { */ public function __construct($config) { $this->publicDir = __DIR__."/../../../".$config["publicDir"]; + $this->sourceDir = "/../../../".$config["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; $this->snapshotsBase = $this->publicDir."/snapshots/"; } @@ -114,9 +115,20 @@ public function takeSnapshot($dir) { $html .= "
  • ".$scanDir."
  • \n"; } + $d = array("html" => $html); + $templateLoader = new TemplateLoader(); + $templateHelper = new TemplateHelper($this->sourceDir); + + // render the viewall template + $v = $templateLoader->vanilla(); + $h = $v->render($templateHelper->mainPageHead); + $f = $v->render($templateHelper->mainPageFoot); + + // render the snapshot page $m = $templateLoader->fileSystem(); - $r = $m->render('snapshot', array("html" => $html)); + $r = $m->render('snapshot', $d); + $r = $h.$r.$f; file_put_contents($this->snapshotsBase."index.html",$r); From 618d7fa3a580df2614808c426535bbd9092f3e1e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:49:37 -0400 Subject: [PATCH 042/166] properly ordering output --- core/lib/PatternLab/Snapshot.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php index f16d21f42..51dea8a5d 100644 --- a/core/lib/PatternLab/Snapshot.php +++ b/core/lib/PatternLab/Snapshot.php @@ -111,6 +111,8 @@ public function takeSnapshot($dir) { $key = array_search('index.html', $scannedDirs); unset($scannedDirs[$key]); + usort($scannedDirs, "strnatcmp"); + foreach ($scannedDirs as $scanDir) { $html .= "
  • ".$scanDir."
  • \n"; } From 4f1b79c245f35a138ab2399e79f594d8ac2ea78d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:49:54 -0400 Subject: [PATCH 043/166] using the template helper for header and footer --- core/lib/PatternLab/Builder.php | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 946b9adfd..df0daf333 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -114,8 +114,8 @@ protected function renderPattern($f,$p) { } - $pattern = $this->pl->render($f,$d); - $escaped = htmlentities($pattern); + $pattern = $this->pl->render($f,$d); + $escaped = htmlentities($pattern); if ($this->addPatternHF) { $patternHead = $this->mv->render($this->patternHead,$d); @@ -962,22 +962,12 @@ protected function gatherPatternInfo() { } - // load pattern-lab's resources - $htmlHead = file_get_contents(__DIR__."/../../templates/pattern-header-footer/header.html"); - $htmlFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer.html"); - $extraFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer-pattern.html"); - - // gather the user-defined header and footer information - $patternHeadPath = __DIR__.$this->sp."00-atoms/00-meta/_00-head.mustache"; - $patternFootPath = __DIR__.$this->sp."00-atoms/00-meta/_01-foot.mustache"; - $patternHead = (file_exists($patternHeadPath)) ? file_get_contents($patternHeadPath) : ""; - $patternFoot = (file_exists($patternFootPath)) ? file_get_contents($patternFootPath) : ""; - // add pattern lab's resource to the user-defined files - $this->patternHead = str_replace("{% pattern-lab-head %}",$htmlHead,$patternHead); - $this->patternFoot = str_replace("{% pattern-lab-foot %}",$extraFoot.$htmlFoot,$patternFoot); - $this->mainPageHead = $this->patternHead; - $this->mainPageFoot = str_replace("{% pattern-lab-foot %}",$htmlFoot,$patternFoot); + $templateHelper = new TemplateHelper($this->sp); + $this->patternHead = $templateHelper->patternHead; + $this->patternFoot = $templateHelper->patternFoot; + $this->mainPageHead = $templateHelper->mainPageHead; + $this->mainPageFoot = $templateHelper->mainPageFoot; } From e319e0174bf447a9c57ef944664dd8b7f80d3392 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:50:41 -0400 Subject: [PATCH 044/166] copy fix --- core/lib/PatternLab/TemplateLoader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/TemplateLoader.php b/core/lib/PatternLab/TemplateLoader.php index 72b62a3e2..c13109bdc 100644 --- a/core/lib/PatternLab/TemplateLoader.php +++ b/core/lib/PatternLab/TemplateLoader.php @@ -13,7 +13,7 @@ namespace PatternLab; class TemplateLoader { - + /** * Load a new Mustache instance that uses the File System Loader * @@ -25,7 +25,7 @@ public static function fileSystem() { "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/partials/") )); } - + /** * Load a new Mustache instance that is just a vanilla Mustache rendering engine * From e0a257bd8e3595727990de00573150d39744db42 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:50:56 -0400 Subject: [PATCH 045/166] adding to templates for snapshots --- core/templates/partials/ishControls.mustache | 2 +- core/templates/snapshot.mustache | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 core/templates/snapshot.mustache diff --git a/core/templates/partials/ishControls.mustache b/core/templates/partials/ishControls.mustache index 83dc12cb6..2dd6b2e9b 100644 --- a/core/templates/partials/ishControls.mustache +++ b/core/templates/partials/ishControls.mustache @@ -53,7 +53,7 @@
      {{^ ishControlsHide.tools-follow }}
    • Page Follow
    • {{/ ishControlsHide.tools-follow }} {{^ ishControlsHide.tools-reload }}
    • Auto-reload
    • {{/ ishControlsHide.tools-reload }} - {{^ ishControlsHide.tools-snapshot }}
    • Snapshots
    • {{/ ishControlsHide.tools-snapshot }} + {{^ ishControlsHide.tools-snapshot }}
    • Snapshots
    • {{/ ishControlsHide.tools-snapshot }} {{^ ishControlsHide.tools-shortcuts }}
    • Keyboard Shortcuts{{/ ishControlsHide.tools-shortcuts }} {{^ ishControlsHide.tools-docs }}
    • Pattern Lab Docs{{/ ishControlsHide.tools-docs }}
    diff --git a/core/templates/snapshot.mustache b/core/templates/snapshot.mustache new file mode 100644 index 000000000..25e428a78 --- /dev/null +++ b/core/templates/snapshot.mustache @@ -0,0 +1,17 @@ + + +
    +

    Snapshots

    +

    These are a list of the snapshots you've made of your Pattern Lab project:

    +
      + {{{ html }}} +
    +
    + + + + From 554508739392ac00f95a7ebf1c03b13f69290dbb Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:51:13 -0400 Subject: [PATCH 046/166] better basic support for pattern partials --- core/styleguide/js/styleguide.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/styleguide/js/styleguide.js b/core/styleguide/js/styleguide.js index 998a66b01..6a08ebba9 100644 --- a/core/styleguide/js/styleguide.js +++ b/core/styleguide/js/styleguide.js @@ -522,7 +522,7 @@ // update the iframe with the source from clicked element in pull down menu. also close the menu // having it outside fixes an auto-close bug i ran into - $('.sg-nav a').not('.sg-acc-handle').on("click", function(e){ + $('a[data-patternpartial]').on("click", function(e){ e.preventDefault(); // update the iframe via the history api handler var obj = JSON.stringify({ "path": urlHandler.getFileName($(this).attr("data-patternpartial")) }); From 7433f119a7f003b3c63a21e1fdf33f5cae045a46 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:51:34 -0400 Subject: [PATCH 047/166] smarter reset of iframe url to avoid caching --- core/styleguide/js/styleguide.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/styleguide/js/styleguide.js b/core/styleguide/js/styleguide.js index 6a08ebba9..6a3aa8712 100644 --- a/core/styleguide/js/styleguide.js +++ b/core/styleguide/js/styleguide.js @@ -495,11 +495,11 @@ // load the iframe source var patternName = "all"; var patternPath = ""; - var iFramePath = window.location.protocol+"//"+window.location.host+window.location.pathname.replace("index.html","")+"styleguide/html/styleguide.html"; + var iFramePath = window.location.protocol+"//"+window.location.host+window.location.pathname.replace("index.html","")+"styleguide/html/styleguide.html?"+Date.now(); if ((oGetVars.p !== undefined) || (oGetVars.pattern !== undefined)) { patternName = (oGetVars.p !== undefined) ? oGetVars.p : oGetVars.pattern; patternPath = urlHandler.getFileName(patternName); - iFramePath = (patternPath !== "") ? window.location.protocol+"//"+window.location.host+window.location.pathname.replace("index.html","")+patternPath : iFramePath; + iFramePath = (patternPath !== "") ? window.location.protocol+"//"+window.location.host+window.location.pathname.replace("index.html","")+patternPath+"?"+Date.now() : iFramePath; } if (patternName !== "all") { From 6690336c0a743f142271f09e26c596a925377045 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:51:47 -0400 Subject: [PATCH 048/166] handle snapshots as a passed parameter --- core/styleguide/js/url-handler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/styleguide/js/url-handler.js b/core/styleguide/js/url-handler.js index e9691a7ea..2e06deb80 100644 --- a/core/styleguide/js/url-handler.js +++ b/core/styleguide/js/url-handler.js @@ -33,6 +33,8 @@ var urlHandler = { if (name == "all") { return "styleguide/html/styleguide.html"; + } else if (name == "snapshots") { + return "snapshots/index.html"; } var paths = (name.indexOf("viewall-") != -1) ? viewAllPaths : patternPaths; From eb3e116f0413af0eff8a8295b7643d7e754e66fa Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:52:42 -0400 Subject: [PATCH 049/166] handling replacement of snapshots --- core/styleguide/js/postmessage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/styleguide/js/postmessage.js b/core/styleguide/js/postmessage.js index 76457e81e..e7598988a 100644 --- a/core/styleguide/js/postmessage.js +++ b/core/styleguide/js/postmessage.js @@ -91,7 +91,7 @@ function receiveIframeMessage(event) { if (patternPartial !== "") { // handle patterns and the view all page - var re = /patterns\/(.*)$/; + var re = /(patterns|snapshots)\/(.*)$/; path = window.location.protocol+"//"+window.location.host+window.location.pathname.replace(re,'')+data.path+'?'+Date.now(); window.location.replace(path); From d4b95e7ae62563cc6c4123793540f4d44f2d2085 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 6 May 2014 15:52:59 -0400 Subject: [PATCH 050/166] smarter handling of _blank & _parent --- core/styleguide/js/postmessage.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/styleguide/js/postmessage.js b/core/styleguide/js/postmessage.js index e7598988a..68a754f30 100644 --- a/core/styleguide/js/postmessage.js +++ b/core/styleguide/js/postmessage.js @@ -30,10 +30,16 @@ if (self != top) { var aTags = document.getElementsByTagName('a'); for (var i = 0; i < aTags.length; i++) { aTags[i].onclick = function(e) { - e.preventDefault(); - var href = this.getAttribute("href"); - if (href != "#") { + var href = this.getAttribute("href"); + var target = this.getAttribute("target"); + if ((target != undefined) && ((target == "_parent") || (target == "_blank"))) { + // just do normal stuff + } else if (href != "#") { + e.preventDefault(); window.location.replace(href); + } else { + e.preventDefault(); + return false; } }; } From e2b2eb9b693bc751f65ecb7bdb0284ed5d63d2cb Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Wed, 7 May 2014 16:04:38 -0400 Subject: [PATCH 051/166] starting to add start kit info --- core/builder.php | 4 ++ core/lib/PatternLab/StarterKit.php | 68 ++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core/lib/PatternLab/StarterKit.php diff --git a/core/builder.php b/core/builder.php index c157b931e..dc9482a26 100644 --- a/core/builder.php +++ b/core/builder.php @@ -56,6 +56,10 @@ $console->setCommand("s","snapshot","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); $console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); +// set-up the fetch command and options +$console->setCommand("s","snapshot","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); +$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); + // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); diff --git a/core/lib/PatternLab/StarterKit.php b/core/lib/PatternLab/StarterKit.php new file mode 100644 index 000000000..95adbff7e --- /dev/null +++ b/core/lib/PatternLab/StarterKit.php @@ -0,0 +1,68 @@ +sourceDir = __DIR__."/../../../".$config["publicDir"]; + } + + /** + * Fetch the starter kit from GitHub and put it into source/ + * @param {String} path of the GitHub repo + * + * @return {String} the modified file contents + */ + public function fetch($org,$repo,$tag) { + + //master + //tag + + //get the path to the GH repo and validate it + $tarballUrl = "https://github.com/".$org."/".$repo."/archive/".$tag.".tar.gzz"; + + // try to download the given starter kit + try { + $starterKit = file_get_contents($tarballUrl); + } catch (\Exception $e) { + throw new \Exception('Something really gone wrong'.$e->getMessage(), 0, $e); + } + + // write the starter kit to the temp directory + $tempFile = tempnam(sys_get_temp_dir(), "pl-sk-archive.tar.gz"); + file_put_contents($tempFile, $starterKit); + + $zip = new ZipArchive; + if ($zip->open('test.zip') === TRUE) { + $zip->extractTo('/my/destination/dir/'); + $zip->close(); + echo 'ok'; + } else { + echo 'failed'; + } + + unlink($tempFile); + + //see if source is empty, if not prompt if they want to delete stuff + //delete everything + //download + //unzip + //unpack into source + + } + +} \ No newline at end of file From 09c286781950c6f105e2fe103731825488a67f9f Mon Sep 17 00:00:00 2001 From: Gabriel Luethje Date: Wed, 7 May 2014 17:37:49 -0700 Subject: [PATCH 052/166] Links with no href attribute shouldn't cause a 404 A 3rd party script I'm using [Chosen](http://harvesthq.github.io/chosen/) generates an `a` element without a `href` attribute, which causes a 404 when clicked unless we check for `href.length` in the onclick handler. --- core/styleguide/js/postmessage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/styleguide/js/postmessage.js b/core/styleguide/js/postmessage.js index 76457e81e..115c85eef 100644 --- a/core/styleguide/js/postmessage.js +++ b/core/styleguide/js/postmessage.js @@ -32,7 +32,7 @@ if (self != top) { aTags[i].onclick = function(e) { e.preventDefault(); var href = this.getAttribute("href"); - if (href != "#") { + if (href.length && href !== "#") { window.location.replace(href); } }; From d009b1ea93a06e69ba99a94172cd60302d14c0b6 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:18:00 -0400 Subject: [PATCH 053/166] adding alchemy and required libs --- core/lib/alchemy/zippy/.gitignore | 6 + core/lib/alchemy/zippy/.travis.yml | 24 + core/lib/alchemy/zippy/CHANGELOG.md | 22 + core/lib/alchemy/zippy/LICENSE | 21 + core/lib/alchemy/zippy/README.md | 80 + core/lib/alchemy/zippy/composer.json | 41 + core/lib/alchemy/zippy/composer.lock | 1092 +++++ .../alchemy/zippy/phpunit-functional.xml.dist | 31 + core/lib/alchemy/zippy/phpunit.xml.dist | 31 + .../Alchemy/Zippy/Adapter/AbstractAdapter.php | 246 ++ .../Zippy/Adapter/AbstractBinaryAdapter.php | 231 + .../Zippy/Adapter/AbstractTarAdapter.php | 424 ++ .../Zippy/Adapter/AdapterContainer.php | 148 + .../Zippy/Adapter/AdapterInterface.php | 131 + .../Zippy/Adapter/BSDTar/TarBSDTarAdapter.php | 79 + .../Adapter/BSDTar/TarBz2BSDTarAdapter.php | 25 + .../Adapter/BSDTar/TarGzBSDTarAdapter.php | 25 + .../Zippy/Adapter/BinaryAdapterInterface.php | 94 + .../Adapter/GNUTar/TarBz2GNUTarAdapter.php | 25 + .../Zippy/Adapter/GNUTar/TarGNUTarAdapter.php | 79 + .../Adapter/GNUTar/TarGzGNUTarAdapter.php | 25 + .../Zippy/Adapter/Resource/FileResource.php | 31 + .../Adapter/Resource/ResourceInterface.php | 23 + .../Adapter/Resource/ZipArchiveResource.php | 31 + .../VersionProbe/AbstractTarVersionProbe.php | 75 + .../VersionProbe/BSDTarVersionProbe.php | 24 + .../VersionProbe/GNUTarVersionProbe.php | 24 + .../VersionProbe/VersionProbeInterface.php | 26 + .../VersionProbe/ZipExtensionVersionProbe.php | 24 + .../Adapter/VersionProbe/ZipVersionProbe.php | 96 + .../src/Alchemy/Zippy/Adapter/ZipAdapter.php | 361 ++ .../Zippy/Adapter/ZipExtensionAdapter.php | 339 ++ .../src/Alchemy/Zippy/Archive/Archive.php | 136 + .../Zippy/Archive/ArchiveInterface.php | 74 + .../src/Alchemy/Zippy/Archive/Member.php | 133 + .../Alchemy/Zippy/Archive/MemberInterface.php | 66 + .../Zippy/Exception/ExceptionInterface.php | 16 + .../Exception/FormatNotSupportedException.php | 16 + .../Alchemy/Zippy/Exception/IOException.php | 16 + .../Exception/InvalidArgumentException.php | 16 + .../NoAdapterOnPlatformException.php | 16 + .../Zippy/Exception/NotSupportedException.php | 16 + .../Zippy/Exception/RuntimeException.php | 16 + .../Exception/TargetLocatorException.php | 28 + .../FileStrategy/FileStrategyInterface.php | 29 + .../Zippy/FileStrategy/TB2FileStrategy.php | 43 + .../Zippy/FileStrategy/TBz2FileStrategy.php | 43 + .../Zippy/FileStrategy/TGzFileStrategy.php | 43 + .../Zippy/FileStrategy/TarBz2FileStrategy.php | 43 + .../Zippy/FileStrategy/TarFileStrategy.php | 43 + .../Zippy/FileStrategy/TarGzFileStrategy.php | 43 + .../Zippy/FileStrategy/ZipFileStrategy.php | 43 + .../Zippy/Parser/BSDTarOutputParser.php | 115 + .../Zippy/Parser/GNUTarOutputParser.php | 98 + .../Alchemy/Zippy/Parser/ParserFactory.php | 45 + .../Alchemy/Zippy/Parser/ParserInterface.php | 46 + .../Alchemy/Zippy/Parser/ZipOutputParser.php | 96 + .../ProcessBuilder/ProcessBuilderFactory.php | 71 + .../ProcessBuilderFactoryInterface.php | 45 + .../Alchemy/Zippy/Resource/RequestMapper.php | 63 + .../src/Alchemy/Zippy/Resource/Resource.php | 106 + .../Zippy/Resource/ResourceCollection.php | 86 + .../Zippy/Resource/ResourceManager.php | 105 + .../Zippy/Resource/ResourceTeleporter.php | 55 + .../Alchemy/Zippy/Resource/TargetLocator.php | 156 + .../Teleporter/AbstractTeleporter.php | 53 + .../Resource/Teleporter/GuzzleTeleporter.php | 74 + .../Resource/Teleporter/LocalTeleporter.php | 71 + .../Resource/Teleporter/StreamTeleporter.php | 61 + .../Teleporter/TeleporterInterface.php | 28 + .../Zippy/Resource/TeleporterContainer.php | 79 + .../alchemy/zippy/src/Alchemy/Zippy/Zippy.php | 213 + core/lib/doctrine/collections/.gitignore | 1 + core/lib/doctrine/collections/.travis.yml | 10 + core/lib/doctrine/collections/LICENSE | 19 + core/lib/doctrine/collections/README.md | 17 + core/lib/doctrine/collections/composer.json | 26 + .../Collections/AbstractLazyCollection.php | 343 ++ .../Common/Collections/ArrayCollection.php | 385 ++ .../Common/Collections/Collection.php | 260 ++ .../Doctrine/Common/Collections/Criteria.php | 245 ++ .../Expr/ClosureExpressionVisitor.php | 227 + .../Common/Collections/Expr/Comparison.php | 103 + .../Collections/Expr/CompositeExpression.php | 90 + .../Common/Collections/Expr/Expression.php | 35 + .../Collections/Expr/ExpressionVisitor.php | 82 + .../Common/Collections/Expr/Value.php | 52 + .../Common/Collections/ExpressionBuilder.php | 166 + .../Common/Collections/Selectable.php | 48 + .../lib/doctrine/collections/phpunit.xml.dist | 31 + core/lib/guzzle/guzzle/.gitignore | 27 + core/lib/guzzle/guzzle/.travis.yml | 30 + core/lib/guzzle/guzzle/CHANGELOG.md | 788 ++++ core/lib/guzzle/guzzle/LICENSE | 19 + core/lib/guzzle/guzzle/README.md | 36 + core/lib/guzzle/guzzle/UPGRADING.md | 537 +++ core/lib/guzzle/guzzle/build.xml | 45 + core/lib/guzzle/guzzle/composer.json | 74 + core/lib/guzzle/guzzle/phar-stub.php | 28 + .../guzzle/guzzle/phing/build.properties.dist | 16 + .../guzzle/phing/imports/dependencies.xml | 33 + .../guzzle/guzzle/phing/imports/deploy.xml | 142 + .../guzzle/phing/tasks/ComposerLintTask.php | 152 + .../phing/tasks/GuzzlePearPharPackageTask.php | 338 ++ .../guzzle/phing/tasks/GuzzleSubSplitTask.php | 385 ++ core/lib/guzzle/guzzle/phpunit.xml.dist | 48 + .../Guzzle/Batch/AbstractBatchDecorator.php | 66 + .../guzzle/guzzle/src/Guzzle/Batch/Batch.php | 92 + .../guzzle/src/Guzzle/Batch/BatchBuilder.php | 199 + .../src/Guzzle/Batch/BatchClosureDivisor.php | 39 + .../src/Guzzle/Batch/BatchClosureTransfer.php | 40 + .../src/Guzzle/Batch/BatchCommandTransfer.php | 75 + .../Guzzle/Batch/BatchDivisorInterface.php | 18 + .../src/Guzzle/Batch/BatchInterface.php | 32 + .../src/Guzzle/Batch/BatchRequestTransfer.php | 65 + .../src/Guzzle/Batch/BatchSizeDivisor.php | 47 + .../Guzzle/Batch/BatchTransferInterface.php | 16 + .../Exception/BatchTransferException.php | 90 + .../Guzzle/Batch/ExceptionBufferingBatch.php | 50 + .../guzzle/src/Guzzle/Batch/FlushingBatch.php | 60 + .../guzzle/src/Guzzle/Batch/HistoryBatch.php | 39 + .../src/Guzzle/Batch/NotifyingBatch.php | 38 + .../guzzle/src/Guzzle/Batch/composer.json | 31 + .../src/Guzzle/Cache/AbstractCacheAdapter.php | 21 + .../src/Guzzle/Cache/CacheAdapterFactory.php | 117 + .../Guzzle/Cache/CacheAdapterInterface.php | 55 + .../src/Guzzle/Cache/ClosureCacheAdapter.php | 57 + .../src/Guzzle/Cache/DoctrineCacheAdapter.php | 41 + .../src/Guzzle/Cache/NullCacheAdapter.php | 31 + .../src/Guzzle/Cache/Zf1CacheAdapter.php | 44 + .../src/Guzzle/Cache/Zf2CacheAdapter.php | 41 + .../guzzle/src/Guzzle/Cache/composer.json | 27 + .../Guzzle/Common/AbstractHasDispatcher.php | 49 + .../guzzle/src/Guzzle/Common/Collection.php | 403 ++ .../guzzle/guzzle/src/Guzzle/Common/Event.php | 52 + .../Exception/BadMethodCallException.php | 5 + .../Common/Exception/ExceptionCollection.php | 108 + .../Common/Exception/GuzzleException.php | 8 + .../Exception/InvalidArgumentException.php | 5 + .../Common/Exception/RuntimeException.php | 5 + .../Exception/UnexpectedValueException.php | 5 + .../src/Guzzle/Common/FromConfigInterface.php | 18 + .../Guzzle/Common/HasDispatcherInterface.php | 54 + .../src/Guzzle/Common/ToArrayInterface.php | 16 + .../guzzle/src/Guzzle/Common/Version.php | 29 + .../guzzle/src/Guzzle/Common/composer.json | 20 + .../Http/AbstractEntityBodyDecorator.php | 221 + .../src/Guzzle/Http/CachingEntityBody.php | 229 + .../guzzle/guzzle/src/Guzzle/Http/Client.php | 490 +++ .../src/Guzzle/Http/ClientInterface.php | 223 + .../src/Guzzle/Http/Curl/CurlHandle.php | 464 ++ .../guzzle/src/Guzzle/Http/Curl/CurlMulti.php | 367 ++ .../Guzzle/Http/Curl/CurlMultiInterface.php | 58 + .../src/Guzzle/Http/Curl/CurlMultiProxy.php | 150 + .../src/Guzzle/Http/Curl/CurlVersion.php | 66 + .../src/Guzzle/Http/Curl/RequestMediator.php | 147 + .../guzzle/src/Guzzle/Http/EntityBody.php | 201 + .../src/Guzzle/Http/EntityBodyInterface.php | 73 + .../Http/Exception/BadResponseException.php | 69 + .../ClientErrorResponseException.php | 8 + .../CouldNotRewindStreamException.php | 7 + .../Guzzle/Http/Exception/CurlException.php | 101 + .../Guzzle/Http/Exception/HttpException.php | 10 + .../Http/Exception/MultiTransferException.php | 145 + .../Http/Exception/RequestException.php | 39 + .../ServerErrorResponseException.php | 8 + .../Exception/TooManyRedirectsException.php | 5 + .../src/Guzzle/Http/IoEmittingEntityBody.php | 83 + .../Guzzle/Http/Message/AbstractMessage.php | 220 + .../Http/Message/EntityEnclosingRequest.php | 247 ++ .../EntityEnclosingRequestInterface.php | 137 + .../guzzle/src/Guzzle/Http/Message/Header.php | 182 + .../Http/Message/Header/CacheControl.php | 121 + .../Http/Message/Header/HeaderCollection.php | 108 + .../Http/Message/Header/HeaderFactory.php | 26 + .../Message/Header/HeaderFactoryInterface.php | 19 + .../Http/Message/Header/HeaderInterface.php | 83 + .../src/Guzzle/Http/Message/Header/Link.php | 93 + .../Guzzle/Http/Message/MessageInterface.php | 102 + .../src/Guzzle/Http/Message/PostFile.php | 124 + .../Guzzle/Http/Message/PostFileInterface.php | 83 + .../src/Guzzle/Http/Message/Request.php | 638 +++ .../Guzzle/Http/Message/RequestFactory.php | 359 ++ .../Http/Message/RequestFactoryInterface.php | 105 + .../Guzzle/Http/Message/RequestInterface.php | 318 ++ .../src/Guzzle/Http/Message/Response.php | 968 +++++ .../guzzle/src/Guzzle/Http/Mimetypes.php | 962 +++++ .../Http/QueryAggregator/CommaAggregator.php | 20 + .../QueryAggregator/DuplicateAggregator.php | 22 + .../Http/QueryAggregator/PhpAggregator.php | 27 + .../QueryAggregatorInterface.php | 22 + .../guzzle/src/Guzzle/Http/QueryString.php | 297 ++ .../src/Guzzle/Http/ReadLimitEntityBody.php | 106 + .../guzzle/src/Guzzle/Http/RedirectPlugin.php | 250 ++ .../src/Guzzle/Http/Resources/cacert.pem | 3785 +++++++++++++++++ .../guzzle/src/Guzzle/Http/StaticClient.php | 157 + .../lib/guzzle/guzzle/src/Guzzle/Http/Url.php | 554 +++ .../guzzle/src/Guzzle/Http/composer.json | 32 + .../src/Guzzle/Inflection/Inflector.php | 38 + .../Guzzle/Inflection/InflectorInterface.php | 27 + .../Guzzle/Inflection/MemoizingInflector.php | 70 + .../Inflection/PreComputedInflector.php | 59 + .../src/Guzzle/Inflection/composer.json | 26 + .../src/Guzzle/Iterator/AppendIterator.php | 19 + .../src/Guzzle/Iterator/ChunkedIterator.php | 56 + .../src/Guzzle/Iterator/FilterIterator.php | 36 + .../src/Guzzle/Iterator/MapIterator.php | 34 + .../Guzzle/Iterator/MethodProxyIterator.php | 27 + .../guzzle/src/Guzzle/Iterator/README.md | 25 + .../guzzle/src/Guzzle/Iterator/composer.json | 27 + .../src/Guzzle/Log/AbstractLogAdapter.php | 16 + .../guzzle/src/Guzzle/Log/ArrayLogAdapter.php | 34 + .../src/Guzzle/Log/ClosureLogAdapter.php | 23 + .../src/Guzzle/Log/LogAdapterInterface.php | 18 + .../src/Guzzle/Log/MessageFormatter.php | 179 + .../src/Guzzle/Log/MonologLogAdapter.php | 34 + .../guzzle/src/Guzzle/Log/PsrLogAdapter.php | 36 + .../guzzle/src/Guzzle/Log/Zf1LogAdapter.php | 24 + .../guzzle/src/Guzzle/Log/Zf2LogAdapter.php | 21 + .../guzzle/src/Guzzle/Log/composer.json | 29 + .../src/Guzzle/Parser/Cookie/CookieParser.php | 131 + .../Parser/Cookie/CookieParserInterface.php | 33 + .../Parser/Message/AbstractMessageParser.php | 58 + .../Guzzle/Parser/Message/MessageParser.php | 110 + .../Parser/Message/MessageParserInterface.php | 27 + .../Parser/Message/PeclHttpMessageParser.php | 48 + .../src/Guzzle/Parser/ParserRegistry.php | 75 + .../Parser/UriTemplate/PeclUriTemplate.php | 26 + .../Guzzle/Parser/UriTemplate/UriTemplate.php | 254 ++ .../UriTemplate/UriTemplateInterface.php | 21 + .../src/Guzzle/Parser/Url/UrlParser.php | 48 + .../Guzzle/Parser/Url/UrlParserInterface.php | 19 + .../guzzle/src/Guzzle/Parser/composer.json | 19 + .../src/Guzzle/Plugin/Async/AsyncPlugin.php | 84 + .../src/Guzzle/Plugin/Async/composer.json | 27 + .../Backoff/AbstractBackoffStrategy.php | 91 + .../AbstractErrorCodeBackoffStrategy.php | 40 + .../Guzzle/Plugin/Backoff/BackoffLogger.php | 76 + .../Guzzle/Plugin/Backoff/BackoffPlugin.php | 126 + .../Backoff/BackoffStrategyInterface.php | 30 + .../Backoff/CallbackBackoffStrategy.php | 47 + .../Backoff/ConstantBackoffStrategy.php | 34 + .../Plugin/Backoff/CurlBackoffStrategy.php | 28 + .../Backoff/ExponentialBackoffStrategy.php | 25 + .../Plugin/Backoff/HttpBackoffStrategy.php | 30 + .../Plugin/Backoff/LinearBackoffStrategy.php | 36 + .../Backoff/ReasonPhraseBackoffStrategy.php | 25 + .../Backoff/TruncatedBackoffStrategy.php | 36 + .../src/Guzzle/Plugin/Backoff/composer.json | 28 + .../Cache/CacheKeyProviderInterface.php | 11 + .../src/Guzzle/Plugin/Cache/CachePlugin.php | 353 ++ .../Plugin/Cache/CacheStorageInterface.php | 43 + .../Plugin/Cache/CallbackCanCacheStrategy.php | 53 + .../Cache/CanCacheStrategyInterface.php | 30 + .../Plugin/Cache/DefaultCacheKeyProvider.php | 46 + .../Plugin/Cache/DefaultCacheStorage.php | 251 ++ .../Plugin/Cache/DefaultCanCacheStrategy.php | 32 + .../Plugin/Cache/DefaultRevalidation.php | 174 + .../Guzzle/Plugin/Cache/DenyRevalidation.php | 19 + .../Plugin/Cache/RevalidationInterface.php | 32 + .../Guzzle/Plugin/Cache/SkipRevalidation.php | 19 + .../src/Guzzle/Plugin/Cache/composer.json | 28 + .../src/Guzzle/Plugin/Cookie/Cookie.php | 538 +++ .../Cookie/CookieJar/ArrayCookieJar.php | 237 ++ .../Cookie/CookieJar/CookieJarInterface.php | 85 + .../Plugin/Cookie/CookieJar/FileCookieJar.php | 65 + .../src/Guzzle/Plugin/Cookie/CookiePlugin.php | 70 + .../Exception/InvalidCookieException.php | 7 + .../src/Guzzle/Plugin/Cookie/composer.json | 27 + .../Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php | 46 + .../src/Guzzle/Plugin/CurlAuth/composer.json | 27 + .../ErrorResponseExceptionInterface.php | 22 + .../ErrorResponse/ErrorResponsePlugin.php | 72 + .../Exception/ErrorResponseException.php | 7 + .../Guzzle/Plugin/ErrorResponse/composer.json | 27 + .../Guzzle/Plugin/History/HistoryPlugin.php | 163 + .../src/Guzzle/Plugin/History/composer.json | 27 + .../src/Guzzle/Plugin/Log/LogPlugin.php | 161 + .../src/Guzzle/Plugin/Log/composer.json | 28 + .../Plugin/Md5/CommandContentMd5Plugin.php | 57 + .../Guzzle/Plugin/Md5/Md5ValidatorPlugin.php | 88 + .../src/Guzzle/Plugin/Md5/composer.json | 27 + .../src/Guzzle/Plugin/Mock/MockPlugin.php | 245 ++ .../src/Guzzle/Plugin/Mock/composer.json | 27 + .../src/Guzzle/Plugin/Oauth/OauthPlugin.php | 306 ++ .../src/Guzzle/Plugin/Oauth/composer.json | 27 + .../guzzle/src/Guzzle/Plugin/composer.json | 44 + .../Guzzle/Service/AbstractConfigLoader.php | 177 + .../Guzzle/Service/Builder/ServiceBuilder.php | 189 + .../Builder/ServiceBuilderInterface.php | 40 + .../Service/Builder/ServiceBuilderLoader.php | 89 + .../Guzzle/Service/CachingConfigLoader.php | 46 + .../guzzle/src/Guzzle/Service/Client.php | 297 ++ .../src/Guzzle/Service/ClientInterface.php | 68 + .../Service/Command/AbstractCommand.php | 390 ++ .../Guzzle/Service/Command/ClosureCommand.php | 41 + .../Service/Command/CommandInterface.php | 128 + .../Command/CreateResponseClassEvent.php | 32 + .../Command/DefaultRequestSerializer.php | 169 + .../Service/Command/DefaultResponseParser.php | 55 + .../Service/Command/Factory/AliasFactory.php | 39 + .../Command/Factory/CompositeFactory.php | 154 + .../Command/Factory/ConcreteClassFactory.php | 47 + .../Command/Factory/FactoryInterface.php | 21 + .../Service/Command/Factory/MapFactory.php | 27 + .../Factory/ServiceDescriptionFactory.php | 71 + .../Request/AbstractRequestVisitor.php | 69 + .../LocationVisitor/Request/BodyVisitor.php | 58 + .../LocationVisitor/Request/HeaderVisitor.php | 44 + .../LocationVisitor/Request/JsonVisitor.php | 63 + .../Request/PostFieldVisitor.php | 18 + .../Request/PostFileVisitor.php | 24 + .../LocationVisitor/Request/QueryVisitor.php | 18 + .../Request/RequestVisitorInterface.php | 31 + .../Request/ResponseBodyVisitor.php | 18 + .../LocationVisitor/Request/XmlVisitor.php | 252 ++ .../Response/AbstractResponseVisitor.php | 26 + .../LocationVisitor/Response/BodyVisitor.php | 23 + .../Response/HeaderVisitor.php | 50 + .../LocationVisitor/Response/JsonVisitor.php | 93 + .../Response/ReasonPhraseVisitor.php | 23 + .../Response/ResponseVisitorInterface.php | 46 + .../Response/StatusCodeVisitor.php | 23 + .../LocationVisitor/Response/XmlVisitor.php | 151 + .../LocationVisitor/VisitorFlyweight.php | 138 + .../Service/Command/OperationCommand.php | 89 + .../Command/OperationResponseParser.php | 195 + .../Command/RequestSerializerInterface.php | 21 + .../Command/ResponseClassInterface.php | 18 + .../Command/ResponseParserInterface.php | 18 + .../Guzzle/Service/ConfigLoaderInterface.php | 22 + .../Guzzle/Service/Description/Operation.php | 547 +++ .../Description/OperationInterface.php | 159 + .../Guzzle/Service/Description/Parameter.php | 925 ++++ .../Service/Description/SchemaFormatter.php | 156 + .../Service/Description/SchemaValidator.php | 291 ++ .../Description/ServiceDescription.php | 271 ++ .../ServiceDescriptionInterface.php | 106 + .../Description/ServiceDescriptionLoader.php | 64 + .../Description/ValidatorInterface.php | 28 + .../Service/Exception/CommandException.php | 7 + .../Exception/CommandTransferException.php | 119 + .../Exception/DescriptionBuilderException.php | 7 + .../InconsistentClientTransferException.php | 38 + .../Exception/ResponseClassException.php | 9 + .../Exception/ServiceBuilderException.php | 7 + .../Exception/ServiceNotFoundException.php | 5 + .../Service/Exception/ValidationException.php | 30 + .../AbstractResourceIteratorFactory.php | 37 + .../CompositeResourceIteratorFactory.php | 67 + .../Resource/MapResourceIteratorFactory.php | 34 + .../src/Guzzle/Service/Resource/Model.php | 64 + .../Service/Resource/ResourceIterator.php | 254 ++ .../Resource/ResourceIteratorApplyBatched.php | 111 + .../Resource/ResourceIteratorClassFactory.php | 60 + .../ResourceIteratorFactoryInterface.php | 30 + .../Resource/ResourceIteratorInterface.php | 61 + .../guzzle/src/Guzzle/Service/composer.json | 29 + .../Guzzle/Stream/PhpStreamRequestFactory.php | 276 ++ .../guzzle/src/Guzzle/Stream/Stream.php | 289 ++ .../src/Guzzle/Stream/StreamInterface.php | 218 + .../Stream/StreamRequestFactoryInterface.php | 24 + .../guzzle/src/Guzzle/Stream/composer.json | 30 + core/lib/pimple/pimple/.gitignore | 1 + core/lib/pimple/pimple/.travis.yml | 6 + core/lib/pimple/pimple/LICENSE | 19 + core/lib/pimple/pimple/README.rst | 159 + core/lib/pimple/pimple/composer.json | 25 + core/lib/pimple/pimple/lib/Pimple.php | 214 + core/lib/pimple/pimple/phpunit.xml.dist | 19 + .../Component/EventDispatcher/.gitignore | 3 + .../Component/EventDispatcher/CHANGELOG.md | 16 + .../ContainerAwareEventDispatcher.php | 202 + .../TraceableEventDispatcherInterface.php | 32 + .../Component/EventDispatcher/Event.php | 129 + .../EventDispatcher/EventDispatcher.php | 185 + .../EventDispatcherInterface.php | 96 + .../EventSubscriberInterface.php | 50 + .../EventDispatcher/GenericEvent.php | 186 + .../ImmutableEventDispatcher.php | 92 + .../Symfony/Component/EventDispatcher/LICENSE | 19 + .../Component/EventDispatcher/README.md | 25 + .../ContainerAwareEventDispatcherTest.php | 244 ++ .../Tests/EventDispatcherTest.php | 346 ++ .../EventDispatcher/Tests/EventTest.php | 84 + .../Tests/GenericEventTest.php | 140 + .../Tests/ImmutableEventDispatcherTest.php | 105 + .../Component/EventDispatcher/composer.json | 38 + .../EventDispatcher/phpunit.xml.dist | 30 + .../Symfony/Component/Filesystem/.gitignore | 3 + .../Symfony/Component/Filesystem/CHANGELOG.md | 23 + .../Exception/ExceptionInterface.php | 23 + .../Exception/FileNotFoundException.php | 34 + .../Filesystem/Exception/IOException.php | 41 + .../Exception/IOExceptionInterface.php | 27 + .../Component/Filesystem/Filesystem.php | 477 +++ .../Symfony/Component/Filesystem/LICENSE | 19 + .../Symfony/Component/Filesystem/README.md | 45 + .../Filesystem/Tests/ExceptionTest.php | 46 + .../Filesystem/Tests/FilesystemTest.php | 907 ++++ .../Filesystem/Tests/FilesystemTestCase.php | 125 + .../Component/Filesystem/composer.json | 31 + .../Component/Filesystem/phpunit.xml.dist | 28 + .../Symfony/Component/Process/.gitignore | 3 + .../Symfony/Component/Process/CHANGELOG.md | 31 + .../Process/Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../Process/Exception/LogicException.php | 21 + .../Exception/ProcessFailedException.php | 49 + .../Exception/ProcessTimedOutException.php | 69 + .../Process/Exception/RuntimeException.php | 21 + .../Component/Process/ExecutableFinder.php | 90 + .../process/Symfony/Component/Process/LICENSE | 19 + .../Component/Process/PhpExecutableFinder.php | 67 + .../Symfony/Component/Process/PhpProcess.php | 73 + .../Symfony/Component/Process/Process.php | 1312 ++++++ .../Component/Process/ProcessBuilder.php | 241 ++ .../Component/Process/ProcessPipes.php | 358 ++ .../Component/Process/ProcessUtils.php | 79 + .../Symfony/Component/Process/README.md | 47 + .../Process/Tests/AbstractProcessTest.php | 873 ++++ .../Process/Tests/NonStopableProcess.php | 37 + .../Process/Tests/PhpExecutableFinderTest.php | 64 + .../Process/Tests/PhpProcessTest.php | 29 + .../PipeStdinInStdoutStdErrStreamSelect.php | 63 + .../Process/Tests/ProcessBuilderTest.php | 196 + .../Tests/ProcessFailedExceptionTest.php | 83 + .../Tests/ProcessInSigchildEnvironment.php | 22 + .../Process/Tests/ProcessUtilsTest.php | 48 + .../Tests/SigchildDisabledProcessTest.php | 224 + .../Tests/SigchildEnabledProcessTest.php | 133 + .../Process/Tests/SignalListener.php | 16 + .../Process/Tests/SimpleProcessTest.php | 171 + .../Symfony/Component/Process/composer.json | 31 + .../Component/Process/phpunit.xml.dist | 28 + 435 files changed, 49665 insertions(+) create mode 100644 core/lib/alchemy/zippy/.gitignore create mode 100644 core/lib/alchemy/zippy/.travis.yml create mode 100644 core/lib/alchemy/zippy/CHANGELOG.md create mode 100644 core/lib/alchemy/zippy/LICENSE create mode 100644 core/lib/alchemy/zippy/README.md create mode 100644 core/lib/alchemy/zippy/composer.json create mode 100644 core/lib/alchemy/zippy/composer.lock create mode 100644 core/lib/alchemy/zippy/phpunit-functional.xml.dist create mode 100644 core/lib/alchemy/zippy/phpunit.xml.dist create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarGzBSDTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BinaryAdapterInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGNUTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/FileResource.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php create mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php create mode 100644 core/lib/doctrine/collections/.gitignore create mode 100644 core/lib/doctrine/collections/.travis.yml create mode 100644 core/lib/doctrine/collections/LICENSE create mode 100644 core/lib/doctrine/collections/README.md create mode 100644 core/lib/doctrine/collections/composer.json create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php create mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php create mode 100644 core/lib/doctrine/collections/phpunit.xml.dist create mode 100644 core/lib/guzzle/guzzle/.gitignore create mode 100644 core/lib/guzzle/guzzle/.travis.yml create mode 100644 core/lib/guzzle/guzzle/CHANGELOG.md create mode 100644 core/lib/guzzle/guzzle/LICENSE create mode 100644 core/lib/guzzle/guzzle/README.md create mode 100644 core/lib/guzzle/guzzle/UPGRADING.md create mode 100644 core/lib/guzzle/guzzle/build.xml create mode 100644 core/lib/guzzle/guzzle/composer.json create mode 100644 core/lib/guzzle/guzzle/phar-stub.php create mode 100644 core/lib/guzzle/guzzle/phing/build.properties.dist create mode 100644 core/lib/guzzle/guzzle/phing/imports/dependencies.xml create mode 100644 core/lib/guzzle/guzzle/phing/imports/deploy.xml create mode 100644 core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php create mode 100644 core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php create mode 100644 core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php create mode 100644 core/lib/guzzle/guzzle/phpunit.xml.dist create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Version.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Request.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Response.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryString.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/composer.json create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php create mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/composer.json create mode 100644 core/lib/pimple/pimple/.gitignore create mode 100644 core/lib/pimple/pimple/.travis.yml create mode 100644 core/lib/pimple/pimple/LICENSE create mode 100644 core/lib/pimple/pimple/README.rst create mode 100644 core/lib/pimple/pimple/composer.json create mode 100644 core/lib/pimple/pimple/lib/Pimple.php create mode 100644 core/lib/pimple/pimple/phpunit.xml.dist create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json create mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json create mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist create mode 100644 core/lib/symfony/process/Symfony/Component/Process/.gitignore create mode 100644 core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/LICENSE create mode 100644 core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Process.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/README.md create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SimpleProcessTest.php create mode 100644 core/lib/symfony/process/Symfony/Component/Process/composer.json create mode 100644 core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist diff --git a/core/lib/alchemy/zippy/.gitignore b/core/lib/alchemy/zippy/.gitignore new file mode 100644 index 000000000..357feac89 --- /dev/null +++ b/core/lib/alchemy/zippy/.gitignore @@ -0,0 +1,6 @@ +/tests/phpunit_report +/nbproject/ +/vendor/ +/docs/build +composer.phar + diff --git a/core/lib/alchemy/zippy/.travis.yml b/core/lib/alchemy/zippy/.travis.yml new file mode 100644 index 000000000..20cd2dcb1 --- /dev/null +++ b/core/lib/alchemy/zippy/.travis.yml @@ -0,0 +1,24 @@ +language: php + +before_script: + - sudo apt-get install bsdtar zip + - composer install --dev --prefer-source + +env: + - ZIPPY_ADAPTER=ZipAdapter + - ZIPPY_ADAPTER=ZipExtensionAdapter + - ZIPPY_ADAPTER=GNUTar\\TarGNUTarAdapter + - ZIPPY_ADAPTER=GNUTar\\TarGzGNUTarAdapter + - ZIPPY_ADAPTER=GNUTar\\TarBz2GNUTarAdapter + - ZIPPY_ADAPTER=BSDTar\\TarBSDTarAdapter + - ZIPPY_ADAPTER=BSDTar\\TarGzBSDTarAdapter + - ZIPPY_ADAPTER=BSDTar\\TarBz2BSDTarAdapter + +php: + - 5.3.3 + - 5.4 + - 5.5 + +script: + - phpunit -v + - phpunit -v -c phpunit-functional.xml.dist diff --git a/core/lib/alchemy/zippy/CHANGELOG.md b/core/lib/alchemy/zippy/CHANGELOG.md new file mode 100644 index 000000000..d54b9798c --- /dev/null +++ b/core/lib/alchemy/zippy/CHANGELOG.md @@ -0,0 +1,22 @@ +CHANGELOG +--------- + +* 0.2.0 (04-04-2014) + + * Fix the use of "teleporter" for local files + * Fix adding a new file using tar adapter ( --append option ) + * Allow all adapters to be instantiated even if they are not supported + * Move support detection logic in distinct classes + * Add support for archives relative path + * Use Symfony Process working directory instead of changing working directory + * Archive in context when a single resource is added + +* 0.1.1 (04-12-2013) + + * Throw exception in case chdir failed + * Use guzzle stream download to handle large files without large memory usage + +* 0.1.0 (11-03-2013) + + * First stable version. + * Support for GNUtar, BSDtar, Zip, PHPZip. diff --git a/core/lib/alchemy/zippy/LICENSE b/core/lib/alchemy/zippy/LICENSE new file mode 100644 index 000000000..b7247e120 --- /dev/null +++ b/core/lib/alchemy/zippy/LICENSE @@ -0,0 +1,21 @@ +Zippy is released under the MIT License : + +Copyright (c) 2012 Alchemy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/core/lib/alchemy/zippy/README.md b/core/lib/alchemy/zippy/README.md new file mode 100644 index 000000000..fe8b974e2 --- /dev/null +++ b/core/lib/alchemy/zippy/README.md @@ -0,0 +1,80 @@ +# Zippy + +A Object-Oriented PHP library to manipulate any archive format (de)compression +through commandline utilities or PHP extension. + +[![Build Status](https://secure.travis-ci.org/alchemy-fr/Zippy.png?branch=master)](http://travis-ci.org/alchemy-fr/Zippy) + +## Adapters + +Zippy currently supports + + - zip + - PHP zip + - GNU tar + - BSD tar + +Which brings support to file types + + - .tar + - .zip + - .tar.gz + - .tar.bz2 + +## API Example + +### Archive listing and extraction : + +```php +use Alchemy\Zippy\Zippy; + +$zippy = Zippy::load(); +$zippy->create('archive.zip', '/path/to/folder'); + +$archive = $zippy->open('build.tar'); + +// extract content to `/tmp` +$archive->extract('/tmp'); + +// iterates through members +foreach ($archive as $member) { + echo "archive contains $member \n"; +} +``` + +### Archive creation + +```php +use Alchemy\Zippy\Zippy; + +$zippy = Zippy::load(); +// creates an archive.zip that contains a directory "folder" that contains +// files contained in "/path/to/directory" recursively +$archive = $zippy->create('archive.zip', array( + 'folder' => '/path/to/directory' +), recursive = true); +``` + +### Customize file and directory names inside archive + +```php +use Alchemy\Zippy\Zippy; + +$zippy = Zippy::load(); +$archive = $zippy->create('archive.zip', array( + 'folder' => '/path/to/directory', // will create a folder at root + 'http://www.google.com/logo.jpg', // will create a logo.jpg file at root + fopen('https://www.facebook.com/index.php'), // will create an index.php at root + 'directory/image.jpg' => 'image.jpg', // will create a image.jpg in 'directory' folder +)); +``` + +##API Browser + +## Documentation + +Documentation hosted at [read the docs](https://zippy.readthedocs.org/) ! + +##License + +This project is licensed under the [MIT license](http://opensource.org/licenses/MIT). diff --git a/core/lib/alchemy/zippy/composer.json b/core/lib/alchemy/zippy/composer.json new file mode 100644 index 000000000..2454aa93b --- /dev/null +++ b/core/lib/alchemy/zippy/composer.json @@ -0,0 +1,41 @@ +{ + "name": "alchemy/zippy", + "type": "library", + "description": "Zippy, the archive manager companion", + "keywords": ["zip", "tar", "bzip", "compression"], + "license": "MIT", + "authors": [ + { + "name": "Alchemy", + "email": "dev.team@alchemy.fr", + "homepage": "http://www.alchemy.fr/" + } + ], + "require": { + "php" : ">=5.3.3", + "doctrine/collections" : "~1.0", + "guzzle/guzzle" : "~3.0", + "pimple/pimple" : "~1.0", + "symfony/process" : "~2.0", + "symfony/filesystem" : "~2.0" + }, + "require-dev": { + "ext-zip" : "*", + "phpunit/phpunit" : "~3.7", + "symfony/finder" : "~2.0", + "sami/sami" : "dev-master@dev" + }, + "suggest": { + "ext-zip" : "To use the ZipExtensionAdapter" + }, + "autoload": { + "psr-0": { + "Alchemy": "src" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.2.x-dev" + } + } +} diff --git a/core/lib/alchemy/zippy/composer.lock b/core/lib/alchemy/zippy/composer.lock new file mode 100644 index 000000000..e297e5d7e --- /dev/null +++ b/core/lib/alchemy/zippy/composer.lock @@ -0,0 +1,1092 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "35caa45c80a3b84aae65b0ce71d21510", + "packages": [ + { + "name": "doctrine/collections", + "version": "v1.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "v1.1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/v1.1", + "reference": "v1.1", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2013-03-07 12:15:54" + }, + { + "name": "guzzle/guzzle", + "version": "v3.7.4", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b170b028c6bb5799640e46c8803015b0f9a45ed9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b170b028c6bb5799640e46c8803015b0f9a45ed9", + "reference": "b170b028c6bb5799640e46c8803015b0f9a45ed9", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": ">=2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "*", + "monolog/monolog": "1.*", + "phpunit/phpunit": "3.7.*", + "psr/log": "1.0.*", + "symfony/class-loader": "*", + "zendframework/zend-cache": "2.0.*", + "zendframework/zend-log": "2.0.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle\\Tests": "tests/", + "Guzzle": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2013-10-02 20:47:00" + }, + { + "name": "pimple/pimple", + "version": "v1.0.2", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Pimple.git", + "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", + "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2013-03-08 08:21:40" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.3.6", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/7fc72a7a346a1887d3968cc1ce5642a15cd182e9", + "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2013-09-19 09:45:20" + }, + { + "name": "symfony/filesystem", + "version": "v2.3.6", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "2b8995042086c5552c94d33b5553c492e9cfc00e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/2b8995042086c5552c94d33b5553c492e9cfc00e", + "reference": "2b8995042086c5552c94d33b5553c492e9cfc00e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2013-09-19 09:45:20" + }, + { + "name": "symfony/process", + "version": "v2.3.6", + "target-dir": "Symfony/Component/Process", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "81191e354fe9dad0451036ccf0fdf283649d3f1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/81191e354fe9dad0451036ccf0fdf283649d3f1e", + "reference": "81191e354fe9dad0451036ccf0fdf283649d3f1e", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "http://symfony.com", + "time": "2013-10-09 21:17:57" + } + ], + "packages-dev": [ + { + "name": "michelf/php-markdown", + "version": "1.3", + "source": { + "type": "git", + "url": "https://github.com/michelf/php-markdown.git", + "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/michelf/php-markdown/zipball/fcdd3e0781ae40c2b9847874e0755ff4f5559688", + "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-lib": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Michelf": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "http://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "http://daringfireball.net/" + } + ], + "description": "PHP Markdown", + "homepage": "http://michelf.ca/projects/php-markdown/", + "keywords": [ + "markdown" + ], + "time": "2013-04-11 18:53:11" + }, + { + "name": "nikic/php-parser", + "version": "v0.9.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1e5e280ae88a27effa2ae4aa2bd088494ed8594f", + "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9-dev" + } + }, + "autoload": { + "psr-0": { + "PHPParser": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "time": "2013-08-25 17:11:40" + }, + { + "name": "phpunit/php-code-coverage", + "version": "1.2.13", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", + "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-token-stream": ">=1.1.3@stable" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*@dev" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2013-09-10 08:14:32" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5180896f51c5b3648ac946b05f9ec02be78a0b23", + "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2012-10-31 18:15:28" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", + "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2013-09-13 04:58:23" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.28", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", + "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~1.2.1", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-timer": ">=1.0.4", + "phpunit/phpunit-mock-objects": "~1.2.0", + "symfony/yaml": "~2.0" + }, + "require-dev": { + "pear-pear/pear": "1.9.4" + }, + "suggest": { + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "phpunit/php-invoker": ">=1.1.0,<1.2.0" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2013-10-17 07:27:40" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-01-13 10:24:48" + }, + { + "name": "sami/sami", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Sami.git", + "reference": "c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Sami/zipball/c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe", + "reference": "c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe", + "shasum": "" + }, + "require": { + "michelf/php-markdown": "~1.3", + "nikic/php-parser": "0.9.*", + "php": ">=5.3.0", + "pimple/pimple": "1.0.*", + "symfony/console": "~2.1", + "symfony/filesystem": "~2.1", + "symfony/finder": "~2.1", + "symfony/process": "~2.1", + "symfony/yaml": "~2.1", + "twig/twig": "1.*" + }, + "bin": [ + "sami.php" + ], + "type": "application", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-0": { + "Sami": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Sami, an API documentation generator", + "homepage": "http://sami.sensiolabs.org", + "keywords": [ + "phpdoc" + ], + "time": "2013-10-18 19:34:10" + }, + { + "name": "symfony/console", + "version": "v2.3.6", + "target-dir": "Symfony/Component/Console", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "f880062d56edefb25b36f2defa65aafe65959dc7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/f880062d56edefb25b36f2defa65aafe65959dc7", + "reference": "f880062d56edefb25b36f2defa65aafe65959dc7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/event-dispatcher": "~2.1" + }, + "suggest": { + "symfony/event-dispatcher": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "http://symfony.com", + "time": "2013-09-25 06:04:15" + }, + { + "name": "symfony/finder", + "version": "v2.3.6", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/a175521f680b178e63c5d0ab87c6b046c0990c3f", + "reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2013-09-19 09:45:20" + }, + { + "name": "symfony/yaml", + "version": "v2.3.6", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/6bb881b948368482e1abf1a75c08bcf88a1c5fc3", + "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2013-09-22 18:04:39" + }, + { + "name": "twig/twig", + "version": "v1.14.2", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Twig.git", + "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Twig/zipball/ca445842fcea4f844d68203ffa2d00f5e3cdea64", + "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.14-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2013-10-30 08:20:53" + } + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": { + "sami/sami": 20 + }, + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": { + "ext-zip": "*" + } +} diff --git a/core/lib/alchemy/zippy/phpunit-functional.xml.dist b/core/lib/alchemy/zippy/phpunit-functional.xml.dist new file mode 100644 index 000000000..5429609ea --- /dev/null +++ b/core/lib/alchemy/zippy/phpunit-functional.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + tests/Alchemy/Zippy/Functional + + + + + vendor + tests + + + + + diff --git a/core/lib/alchemy/zippy/phpunit.xml.dist b/core/lib/alchemy/zippy/phpunit.xml.dist new file mode 100644 index 000000000..005cf4fc9 --- /dev/null +++ b/core/lib/alchemy/zippy/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + tests/Alchemy/Zippy/Tests + + + + + vendor + tests + + + + + diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php new file mode 100644 index 000000000..4060264fa --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php @@ -0,0 +1,246 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Archive\Archive; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Resource\ResourceManager; +use Alchemy\Zippy\Adapter\VersionProbe\VersionProbeInterface; +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; + +abstract class AbstractAdapter implements AdapterInterface +{ + /** @var ResourceManager */ + protected $manager; + + /** + * The version probe + * + * @var VersionProbeInterface + */ + protected $probe; + + public function __construct(ResourceManager $manager) + { + $this->manager = $manager; + } + + /** + * @inheritdoc + */ + public function open($path) + { + $this->requireSupport(); + + return new Archive($this->createResource($path), $this, $this->manager); + } + + /** + * @inheritdoc + */ + public function create($path, $files = null, $recursive = true) + { + $this->requireSupport(); + + return $this->doCreate($this->makeTargetAbsolute($path), $files, $recursive); + } + + /** + * @inheritdoc + */ + public function listMembers(ResourceInterface $resource) + { + $this->requireSupport(); + + return $this->doListMembers($resource); + } + + /** + * @inheritdoc + */ + public function add(ResourceInterface $resource, $files, $recursive = true) + { + $this->requireSupport(); + + return $this->doAdd($resource, $files, $recursive); + } + + /** + * @inheritdoc + */ + public function remove(ResourceInterface $resource, $files) + { + $this->requireSupport(); + + return $this->doRemove($resource, $files); + } + + /** + * @inheritdoc + */ + public function extract(ResourceInterface $resource, $to = null) + { + $this->requireSupport(); + + return $this->doExtract($resource, $to); + } + + /** + * @inheritdoc + */ + public function extractMembers(ResourceInterface $resource, $members, $to = null) + { + $this->requireSupport(); + + return $this->doExtractMembers($resource, $members, $to); + } + + /** + * Returns the version probe used by this adapter + * + * @return VersionProbeInterface + */ + public function getVersionProbe() + { + return $this->probe; + } + + /** + * Sets the version probe used by this adapter + * + * @return VersionProbeInterface + */ + public function setVersionProbe(VersionProbeInterface $probe) + { + $this->probe = $probe; + + return $this; + } + + /** + * @inheritdoc + */ + public function isSupported() + { + if (!$this->probe) { + throw new RuntimeException(sprintf( + 'No version probe has been set on %s whereas it is required', get_class($this) + )); + } + + return VersionProbeInterface::PROBE_OK === $this->probe->getStatus(); + } + + /** + * Throws an exception is the current adapter is not supported + * + * @throws RuntimeException + */ + protected function requireSupport() + { + if (false === $this->isSupported()) { + throw new RuntimeException(sprintf('%s is not supported on your system', get_class($this))); + } + } + + /** + * Change current working directory to another + * + * @param string $target the target directory + * + * @return AdapterInterface + * + * @throws RuntimeException In case of failure + */ + protected function chdir($target) + { + if (false === @chdir($target)) { + throw new RuntimeException(sprintf('Unable to chdir to `%s`', $target)); + } + + return $this; + } + + /** + * Creates a resource given a path + * + * @return ResourceInterface + */ + abstract protected function createResource($path); + + /** + * Do the removal after having check that the current adapter is supported + * + * @return Array + */ + abstract protected function doRemove(ResourceInterface $resource, $files); + + /** + * Do the add after having check that the current adapter is supported + * + * @return Array + */ + abstract protected function doAdd(ResourceInterface $resource, $files, $recursive); + + /** + * Do the extract after having check that the current adapter is supported + * + * @return \SplFileInfo The extracted archive + */ + abstract protected function doExtract(ResourceInterface $resource, $to); + + /** + * Do the extract members after having check that the current adapter is supported + * + * @return \SplFileInfo The extracted archive + */ + abstract protected function doExtractMembers(ResourceInterface $resource, $members, $to); + + /** + * Do the list members after having check that the current adapter is supported + * + * @return Array + */ + abstract protected function doListMembers(ResourceInterface $resource); + + /** + * Do the create after having check that the current adapter is supported + * + * @return ArchiveInterface + */ + abstract protected function doCreate($path, $file, $recursive); + + /** + * Makes the target path absolute as the adapters might have a different directory + * + * @param $path The path to convert + * + * @return string The absolute path + * + * @throws InvalidArgumentException In case the path is not writable or does not exist + */ + private function makeTargetAbsolute($path) + { + $directory = dirname($path); + + if (!is_dir($directory)) { + throw new InvalidArgumentException(sprintf('Target path %s does not exist.', $directory)); + } + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Target path %s is not writeable.', $directory)); + } + + return realpath($directory).'/'.basename ($path); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php new file mode 100644 index 000000000..8aa1ff38f --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Adapter\Resource\FileResource; +use Alchemy\Zippy\Archive\MemberInterface; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\Parser\ParserInterface; +use Alchemy\Zippy\Parser\ParserFactory; +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactory; +use Alchemy\Zippy\Resource\ResourceManager; +use Symfony\Component\Process\ExecutableFinder; +use Symfony\Component\Process\ProcessBuilder; + +abstract class AbstractBinaryAdapter extends AbstractAdapter implements BinaryAdapterInterface +{ + /** + * The parser to use to parse command output + * + * @var ParserInterface + */ + protected $parser; + + /** + * The deflator process builder factory to use to build binary command line + * + * @var ProcessBuilderFactoryInterface + */ + protected $deflator; + + /** + * The inflator process builder factory to use to build binary command line + * + * @var ProcessBuilderFactoryInterface + */ + protected $inflator; + + /** + * Constructor + * + * @param ParserInterface $parser An output parser + * @param ResourceManager $manager A resource manager + * @param ProcessBuilderFactoryInterface $inflator A process builder factory for the inflator binary + * @param ProcessBuilderFactoryInterface|null $deflator A process builder factory for the deflator binary + */ + public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) + { + $this->parser = $parser; + $this->manager = $manager; + $this->deflator = $deflator; + $this->inflator = $inflator; + } + + /** + * @inheritdoc + */ + public function getParser() + { + return $this->parser; + } + + /** + * @inheritdoc + */ + public function setParser(ParserInterface $parser) + { + $this->parser = $parser; + + return $this; + } + + /** + * @inheritdoc + */ + public function getDeflator() + { + return $this->deflator; + } + + /** + * @inheritdoc + */ + public function getInflator() + { + return $this->inflator; + } + + /** + * @inheritdoc + */ + public function setDeflator(ProcessBuilderFactoryInterface $processBuilder) + { + $this->deflator = $processBuilder; + + return $this; + } + + public function setInflator(ProcessBuilderFactoryInterface $processBuilder) + { + $this->inflator = $processBuilder; + + return $this; + } + + /** + * @inheritdoc + */ + public function getInflatorVersion() + { + $this->requireSupport(); + + return $this->doGetInflatorVersion(); + } + + /** + * @inheritdoc + */ + public function getDeflatorVersion() + { + $this->requireSupport(); + + return $this->doGetDeflatorVersion(); + } + + /** + * Returns a new instance of the invoked adapter + * + * @params String|null $inflatorBinaryName The inflator binary name to use + * @params String|null $deflatorBinaryName The deflator binary name to use + * + * @return AbstractBinaryAdapter + * + * @throws RuntimeException In case object could not be instanciated + */ + public static function newInstance(ExecutableFinder $finder, ResourceManager $manager, $inflatorBinaryName = null, $deflatorBinaryName = null) + { + $inflator = $inflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $inflatorBinaryName : self::findABinary($inflatorBinaryName, static::getDefaultInflatorBinaryName(), $finder); + $deflator = $deflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $deflatorBinaryName : self::findABinary($deflatorBinaryName, static::getDefaultDeflatorBinaryName(), $finder); + + try { + $outputParser = ParserFactory::create(static::getName()); + } catch (InvalidArgumentException $e) { + throw new RuntimeException(sprintf( + 'Failed to get a new instance of %s', + get_called_class()), $e->getCode(), $e + ); + } + + if (null === $inflator) { + throw new RuntimeException(sprintf('Unable to create the inflator')); + } + + if (null === $deflator) { + throw new RuntimeException(sprintf('Unable to create the deflator')); + } + + return new static($outputParser, $manager, $inflator, $deflator); + } + + private static function findABinary($wish, array $defaults, ExecutableFinder $finder) + { + $possibles = $wish ? (array) $wish : $defaults; + + $binary = null; + + foreach ($possibles as $possible) { + if (null !== $found = $finder->find($possible)) { + $binary = new ProcessBuilderFactory($found); + break; + } + } + + return $binary; + } + + /** + * Adds files to argument list + * + * @param Array $files An array of files + * @param ProcessBuilder $builder A Builder instance + * + * @return Boolean + */ + protected function addBuilderFileArgument(array $files, ProcessBuilder $builder) + { + $iterations = 0; + + array_walk($files, function ($file) use ($builder, &$iterations) { + $builder->add( + $file instanceof \SplFileInfo ? + $file->getRealpath() : + ($file instanceof MemberInterface ? $file->getLocation() : $file) + ); + + $iterations++; + }); + + return 0 !== $iterations; + } + + protected function createResource($path) + { + return new FileResource($path); + } + + /** + * Fetch the inflator version after having check that the current adapter is supported + * + * @return string + */ + abstract protected function doGetInflatorVersion(); + + /** + * Fetch the Deflator version after having check that the current adapter is supported + * + * @return string + */ + abstract protected function doGetDeflatorVersion(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php new file mode 100644 index 000000000..f865b2c49 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php @@ -0,0 +1,424 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; +use Alchemy\Zippy\Archive\Archive; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Archive\Member; +use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException; + +abstract class AbstractTarAdapter extends AbstractBinaryAdapter +{ + /** + * @inheritdoc + */ + protected function doCreate($path, $files, $recursive) + { + return $this->doTarCreate($this->getLocalOptions(), $path, $files, $recursive); + } + + /** + * @inheritdoc + */ + protected function doListMembers(ResourceInterface $resource) + { + return $this->doTarListMembers($this->getLocalOptions(), $resource); + } + + /** + * @inheritdoc + */ + protected function doAdd(ResourceInterface $resource, $files, $recursive) + { + return $this->doTarAdd($this->getLocalOptions(), $resource, $files, $recursive); + } + + /** + * @inheritdoc + */ + protected function doRemove(ResourceInterface $resource, $files) + { + return $this->doTarRemove($this->getLocalOptions(), $resource, $files); + } + + /** + * @inheritdoc + */ + protected function doExtractMembers(ResourceInterface $resource, $members, $to) + { + return $this->doTarExtractMembers($this->getLocalOptions(), $resource, $members, $to); + } + + /** + * @inheritdoc + */ + protected function doExtract(ResourceInterface $resource, $to) + { + return $this->doTarExtract($this->getLocalOptions(), $resource, $to); + } + + /** + * @inheritdoc + */ + protected function doGetInflatorVersion() + { + $process = $this + ->inflator + ->create() + ->add('--version') + ->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), $process->getErrorOutput() + )); + } + + return $this->parser->parseInflatorVersion($process->getOutput() ? : ''); + } + + /** + * @inheritdoc + */ + protected function doGetDeflatorVersion() + { + return $this->getInflatorVersion(); + } + + protected function doTarCreate($options, $path, $files = null, $recursive = true) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + if (!$recursive) { + $builder->add('--no-recursion'); + } + + $builder->add('--create'); + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + if (0 === count($files)) { + $nullFile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null'; + + $builder->add('-'); + $builder->add(sprintf('--files-from %s', $nullFile)); + $builder->add(sprintf('> %s', $path)); + + $process = $builder->getProcess(); + $process->run(); + + } else { + + $builder->add(sprintf('--file=%s', $path)); + + if (!$recursive) { + $builder->add('--no-recursion'); + } + + $collection = $this->manager->handle(getcwd(), $files); + + $builder->setWorkingDirectory($collection->getContext()); + + $collection->forAll(function ($i, Resource $resource) use ($builder) { + return $builder->add($resource->getTarget()); + }); + + $process = $builder->getProcess(); + + try { + $process->run(); + } catch (ProcessException $e) { + $this->manager->cleanup($collection); + throw $e; + } + + $this->manager->cleanup($collection); + } + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return new Archive($this->createResource($path), $this, $this->manager); + } + + protected function doTarListMembers($options, ResourceInterface $resource) + { + $builder = $this + ->inflator + ->create(); + + foreach ($this->getListMembersOptions() as $option) { + $builder->add($option); + } + + $builder + ->add('--list') + ->add('-v') + ->add(sprintf('--file=%s', $resource->getResource())); + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + $process = $builder->getProcess(); + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + $members = array(); + + foreach ($this->parser->parseFileListing($process->getOutput() ? : '') as $member) { + $members[] = new Member( + $resource, + $this, + $member['location'], + $member['size'], + $member['mtime'], + $member['is_dir'] + ); + } + + return $members; + } + + protected function doTarAdd($options, ResourceInterface $resource, $files, $recursive = true) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + if (!$recursive) { + $builder->add('--no-recursion'); + } + + $builder + ->add('--append') + ->add(sprintf('--file=%s', $resource->getResource())); + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + // there will be an issue if the file starts with a dash + // see --add-file=FILE + $collection = $this->manager->handle(getcwd(), $files); + + $builder->setWorkingDirectory($collection->getContext()); + + $collection->forAll(function ($i, Resource $resource) use ($builder) { + return $builder->add($resource->getTarget()); + }); + + $process = $builder->getProcess(); + + try { + $process->run(); + } catch (ProcessException $e) { + $this->manager->cleanup($collection); + throw $e; + } + + $this->manager->cleanup($collection); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $files; + } + + protected function doTarRemove($options, ResourceInterface $resource, $files) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + $builder + ->add('--delete') + ->add(sprintf('--file=%s', $resource->getResource())); + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + if (!$this->addBuilderFileArgument($files, $builder)) { + throw new InvalidArgumentException('Invalid files'); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $files; + } + + protected function doTarExtract($options, ResourceInterface $resource, $to = null) + { + if (null !== $to && !is_dir($to)) { + throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); + } + + $builder = $this + ->inflator + ->create(); + + $builder + ->add('--extract') + ->add(sprintf('--file=%s', $resource->getResource())); + + foreach ($this->getExtractOptions() as $option) { + $builder + ->add($option); + } + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + if (null !== $to) { + $builder + ->add('--directory') + ->add($to); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return new \SplFileInfo($to ? : $resource->getResource()); + } + + protected function doTarExtractMembers($options, ResourceInterface $resource, $members, $to = null) + { + if (null !== $to && !is_dir($to)) { + throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); + } + + $members = (array) $members; + + $builder = $this + ->inflator + ->create(); + + $builder + ->add('--extract') + ->add(sprintf('--file=%s', $resource->getResource())); + + foreach ($this->getExtractMembersOptions() as $option) { + $builder + ->add($option); + } + + foreach ((array) $options as $option) { + $builder->add((string) $option); + } + + if (null !== $to) { + $builder + ->add('--directory') + ->add($to); + } + + if (!$this->addBuilderFileArgument($members, $builder)) { + throw new InvalidArgumentException('Invalid files'); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $members; + } + + /** + * Returns an array of option for the listMembers command + * + * @return Array + */ + abstract protected function getListMembersOptions(); + + /** + * Returns an array of option for the extract command + * + * @return Array + */ + abstract protected function getExtractOptions(); + + /** + * Returns an array of option for the extractMembers command + * + * @return Array + */ + abstract protected function getExtractMembersOptions(); + + /** + * Gets adapter specific additional options + * + * @return Array + */ + abstract protected function getLocalOptions(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php new file mode 100644 index 000000000..ee3b18890 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Adapter\BSDTar\TarBSDTarAdapter; +use Alchemy\Zippy\Adapter\BSDTar\TarGzBSDTarAdapter; +use Alchemy\Zippy\Adapter\BSDTar\TarBz2BSDTarAdapter; +use Alchemy\Zippy\Adapter\GNUTar\TarGNUTarAdapter; +use Alchemy\Zippy\Adapter\GNUTar\TarGzGNUTarAdapter; +use Alchemy\Zippy\Adapter\GNUTar\TarBz2GNUTarAdapter; +use Alchemy\Zippy\Resource\ResourceManager; +use Alchemy\Zippy\Resource\RequestMapper; +use Alchemy\Zippy\Resource\TeleporterContainer; +use Alchemy\Zippy\Resource\ResourceTeleporter; +use Alchemy\Zippy\Resource\TargetLocator; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Process\ExecutableFinder; + +class AdapterContainer extends \Pimple +{ + /** + * Builds the adapter container + * + * @return AdapterContainer + */ + public static function load() + { + $container = new static(); + + $container['zip.inflator'] = null; + $container['zip.deflator'] = null; + + $container['resource-manager'] = $container->share(function ($container) { + return new ResourceManager( + $container['request-mapper'], + $container['resource-teleporter'], + $container['filesystem'] + ); + }); + + $container['executable-finder'] = $container->share(function ($container) { + return new ExecutableFinder(); + }); + + $container['request-mapper'] = $container->share(function ($container) { + return new RequestMapper($container['target-locator']); + }); + + $container['target-locator'] = $container->share(function () { + return new TargetLocator(); + }); + + $container['teleporter-container'] = $container->share(function ($container) { + return TeleporterContainer::load(); + }); + + $container['resource-teleporter'] = $container->share(function ($container) { + return new ResourceTeleporter($container['teleporter-container']); + }); + + $container['filesystem'] = $container->share(function () { + return new Filesystem(); + }); + + $container['Alchemy\\Zippy\\Adapter\\ZipAdapter'] = $container->share(function ($container) { + return ZipAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['zip.inflator'], + $container['zip.deflator'] + ); + }); + + $container['gnu-tar.inflator'] = null; + $container['gnu-tar.deflator'] = null; + + $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter'] = $container->share(function ($container) { + return TarGNUTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['gnu-tar.inflator'], + $container['gnu-tar.deflator'] + ); + }); + + $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'] = $container->share(function ($container) { + return TarGzGNUTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['gnu-tar.inflator'], + $container['gnu-tar.deflator'] + ); + }); + + $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'] = $container->share(function ($container) { + return TarBz2GNUTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['gnu-tar.inflator'], + $container['gnu-tar.deflator'] + ); + }); + + $container['bsd-tar.inflator'] = null; + $container['bsd-tar.deflator'] = null; + + $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'] = $container->share(function ($container) { + return TarBSDTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['bsd-tar.inflator'], + $container['bsd-tar.deflator'] + ); + }); + + $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'] = $container->share(function ($container) { + return TarGzBSDTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['bsd-tar.inflator'], + $container['bsd-tar.deflator'] + ); + }); + + $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'] = $container->share(function ($container) { + return TarBz2BSDTarAdapter::newInstance( + $container['executable-finder'], + $container['resource-manager'], + $container['bsd-tar.inflator'], + $container['bsd-tar.deflator']); + }); + + $container['Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'] = $container->share(function () { + return ZipExtensionAdapter::newInstance(); + }); + + return $container; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php new file mode 100644 index 000000000..aaa0a3d24 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Archive\ArchiveInterface; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Exception\RuntimeException; + +Interface AdapterInterface +{ + /** + * Opens an archive + * + * @param String $path The path to the archive + * + * @return ArchiveInterface + * + * @throws InvalidArgumentException In case the provided path is not valid + * @throws RuntimeException In case of failure + */ + public function open($path); + + /** + * Creates a new archive + * + * Please note some adapters can not create empty archives. + * They would throw a `NotSupportedException` in case you ask to create an archive without files + * + * @param String $path The path to the archive + * @param String|Array|\Traversable|null $files A filename, an array of files, or a \Traversable instance + * @param Boolean $recursive Whether to recurse or not in the provided directories + * + * @return ArchiveInterface + * + * @throws RuntimeException In case of failure + * @throws NotSupportedException In case the operation in not supported + * @throws InvalidArgumentException In case no files could be added + */ + public function create($path, $files = null, $recursive = true); + + /** + * Tests if the adapter is supported by the current environment + * + * @return Boolean + */ + public function isSupported(); + + /** + * Returns the list of all archive members + * + * @param ResourceInterface $resource The path to the archive + * + * @return Array + * + * @throws RuntimeException In case of failure + */ + public function listMembers(ResourceInterface $resource); + + /** + * Adds a file to the archive + * + * @param ResourceInterface $resource The path to the archive + * @param String|Array|\Traversable $files An array of paths to add, relative to cwd + * @param Boolean $recursive Whether or not to recurse in the provided directories + * + * @return Array + * + * @throws RuntimeException In case of failure + * @throws InvalidArgumentException In case no files could be added + */ + public function add(ResourceInterface $resource, $files, $recursive = true); + + /** + * Removes a member of the archive + * + * @param ResourceInterface $resource The path to the archive + * @param String|Array|\Traversable $files A filename, an array of files, or a \Traversable instance + * + * @return Array + * + * @throws RuntimeException In case of failure + * @throws InvalidArgumentException In case no files could be removed + */ + public function remove(ResourceInterface $resource, $files); + + /** + * Extracts an entire archive + * + * Note that any existing files will be overwritten by the adapter + * + * @param ResourceInterface $resource The path to the archive + * @param String|null $to The path where to extract the archive + * + * @return \SplFileInfo The extracted archive + * + * @throws RuntimeException In case of failure + * @throws InvalidArgumentException In case the provided path where to extract the archive is not valid + */ + public function extract(ResourceInterface $resource, $to = null); + + /** + * Extracts specific members of the archive + * + * @param ResourceInterface $resource The path to the archive + * @param Array $members An array of members + * @param String|null $to The path where to extract the members + * + * @return \SplFileInfo The extracted archive + * + * @throws RuntimeException In case of failure + * @throws InvalidArgumentException In case no members could be removed or provide extract target directory is not valid + */ + public function extractMembers(ResourceInterface $resource, $members, $to = null); + + /** + * Returns the adapter name + * + * @return String + */ + public static function getName(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php new file mode 100644 index 000000000..13a523fd1 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php @@ -0,0 +1,79 @@ +probe = new BSDTarVersionProbe($inflator, $deflator); + } + + /** + * @inheritdoc + */ + protected function getLocalOptions() + { + return array(); + } + + /** + * @inheritdoc + */ + public static function getName() + { + return 'bsd-tar'; + } + + /** + * @inheritdoc + */ + public static function getDefaultDeflatorBinaryName() + { + return array('bsdtar', 'tar'); + } + + /** + * @inheritdoc + */ + public static function getDefaultInflatorBinaryName() + { + return array('bsdtar', 'tar'); + } + + /** + * {@inheritdoc} + */ + protected function getListMembersOptions() + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getExtractOptions() + { + return array(); + } + + /** + * {@inheritdoc} + */ + protected function getExtractMembersOptions() + { + return array(); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php new file mode 100644 index 000000000..b2c8d8dea --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Parser\ParserInterface; +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; + +interface BinaryAdapterInterface +{ + /** + * Gets the output parser + * + * @return ParserInterface + */ + public function getParser(); + + /** + * Sets the parser + * + * @param ParserInterface $parser The parser to use + * + * @return AbstractBinaryAdapter + */ + public function setParser(ParserInterface $parser); + + /** + * Returns the inflator process builder + * + * @return ProcessBuilderFactoryInterface + */ + public function getInflator(); + + /** + * Sets the inflator process builder + * + * @param ProcessBuilderFactoryInterface $processBuilder The parser to use + * + * @return AbstractBinaryAdapter + */ + public function setInflator(ProcessBuilderFactoryInterface $processBuilder); + + /** + * Returns the deflator process builder + * + * @return ProcessBuilderFactoryInterface + */ + public function getDeflator(); + + /** + * Sets the deflator process builder + * + * @param ProcessBuilderFactoryInterface $processBuilder The parser to use + * + * @return AbstractBinaryAdapter + */ + public function setDeflator(ProcessBuilderFactoryInterface $processBuilder); + + /** + * Returns the inflator binary version + * + * @return String + */ + public function getInflatorVersion(); + + /** + * Returns the deflator binary version + * + * @return String + */ + public function getDeflatorVersion(); + + /** + * Gets the inflator adapter binary name + * + * @return Array + */ + public static function getDefaultInflatorBinaryName(); + + /** + * Gets the deflator adapter binary name + * + * @return Array + */ + public static function getDefaultDeflatorBinaryName(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php new file mode 100644 index 000000000..d44dc2575 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php @@ -0,0 +1,25 @@ +probe = new GNUTarVersionProbe($inflator, $deflator); + } + + /** + * @inheritdoc + */ + protected function getLocalOptions() + { + return array(); + } + + /** + * @inheritdoc + */ + public static function getName() + { + return 'gnu-tar'; + } + + /** + * @inheritdoc + */ + public static function getDefaultDeflatorBinaryName() + { + return array('gnutar', 'tar'); + } + + /** + * @inheritdoc + */ + public static function getDefaultInflatorBinaryName() + { + return array('gnutar', 'tar'); + } + + /** + * {@inheritdoc} + */ + protected function getListMembersOptions() + { + return array('--utc'); + } + + /** + * {@inheritdoc} + */ + protected function getExtractOptions() + { + return array('--overwrite-dir', '--overwrite', '--strip-components=1'); + } + + /** + * {@inheritdoc} + */ + protected function getExtractMembersOptions() + { + return array('--overwrite-dir', '--overwrite', '--strip-components=1'); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php new file mode 100644 index 000000000..6bbf5cb44 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\Resource; + +class FileResource implements ResourceInterface +{ + private $path; + + public function __construct($path) + { + $this->path = $path; + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->path; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php new file mode 100644 index 000000000..a42d08a5c --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\Resource; + +interface ResourceInterface +{ + /** + * Returns the actual resource used by an adapter + * + * @return mixed + */ + public function getResource(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php new file mode 100644 index 000000000..4028324c4 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\Resource; + +class ZipArchiveResource implements ResourceInterface +{ + private $archive; + + public function __construct(\ZipArchive $archive) + { + $this->archive = $archive; + } + + /** + * {@inheritdoc} + */ + public function getResource() + { + return $this->archive; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php new file mode 100644 index 000000000..602d42006 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; + +abstract class AbstractTarVersionProbe implements VersionProbeInterface +{ + private $isSupported; + private $inflator; + private $deflator; + + public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) + { + $this->inflator = $inflator; + $this->deflator = $deflator; + } + + /** + * {@inheritdoc} + */ + public function getStatus() + { + if (null !== $this->isSupported) { + return $this->isSupported; + } + + if (null === $this->inflator || null === $this->deflator) { + return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; + } + + $good = true; + + foreach (array($this->inflator, $this->deflator) as $builder) { + $process = $builder + ->create() + ->add('--version') + ->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; + } + + $lines = explode("\n", $process->getOutput(), 2); + $good = false !== stripos($lines[0], $this->getVersionSignature()); + + if (!$good) { + break; + } + } + + $this->isSupported = $good ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; + + return $this->isSupported; + } + + /** + * Returns the signature of inflator/deflator + * + * @return string + */ + abstract protected function getVersionSignature(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php new file mode 100644 index 000000000..6f374750e --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +class BSDTarVersionProbe extends AbstractTarVersionProbe +{ + /** + * {@inheritdoc} + */ + protected function getVersionSignature() + { + return 'bsdtar'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php new file mode 100644 index 000000000..046a836ad --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +class GNUTarVersionProbe extends AbstractTarVersionProbe +{ + /** + * {@inheritdoc} + */ + protected function getVersionSignature() + { + return '(gnu tar)'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php new file mode 100644 index 000000000..a0c03721b --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +interface VersionProbeInterface +{ + const PROBE_OK = 0; + const PROBE_NOTSUPPORTED = 1; + + /** + * Probes for the support of an adapter. + * + * @return integer One of the self::PROBE_* constants + */ + public function getStatus(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php new file mode 100644 index 000000000..d95d0b53d --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +class ZipExtensionVersionProbe implements VersionProbeInterface +{ + /** + * {@inheritdoc} + */ + public function getStatus() + { + return class_exists('\ZipArchive') ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php new file mode 100644 index 000000000..12595cbf9 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + * + */ + +namespace Alchemy\Zippy\Adapter\VersionProbe; + +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; + +class ZipVersionProbe implements VersionProbeInterface +{ + private $isSupported; + private $inflator; + private $deflator; + + public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) + { + $this->inflator = $inflator; + $this->deflator = $deflator; + } + + /** + * Set the inflator to zip + * + * @param ProcessBuilderFactoryInterface $inflator + * @return ZipVersionProbe + */ + public function setInflator(ProcessBuilderFactoryInterface $inflator) + { + $this->inflator = $inflator; + + return $this; + } + + /** + * Set the deflator to unzip + * + * @param ProcessBuilderFactoryInterface $deflator + * @return ZipVersionProbe + */ + public function setDeflator(ProcessBuilderFactoryInterface $deflator) + { + $this->deflator = $deflator; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getStatus() + { + if (null !== $this->isSupported) { + return $this->isSupported; + } + + if (null === $this->inflator || null === $this->deflator) { + return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; + } + + $processDeflate = $this + ->deflator + ->create() + ->add('-h') + ->getProcess(); + + $processDeflate->run(); + + $processInflate = $this + ->inflator + ->create() + ->add('-h') + ->getProcess(); + + $processInflate->run(); + + if (false === $processDeflate->isSuccessful() || false === $processInflate->isSuccessful()) { + return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; + } + + $lines = explode("\n", $processInflate->getOutput(), 2); + $inflatorOk = false !== stripos($lines[0], 'Info-ZIP'); + + $lines = explode("\n", $processDeflate->getOutput(), 2); + $deflatorOk = false !== stripos($lines[0], 'Info-ZIP'); + + return $this->isSupported = ($inflatorOk && $deflatorOk) ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php new file mode 100644 index 000000000..3d3a0353a --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php @@ -0,0 +1,361 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Archive\Archive; +use Alchemy\Zippy\Archive\Member; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\Exception\NotSupportedException; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Resource\ResourceManager; +use Alchemy\Zippy\Adapter\VersionProbe\ZipVersionProbe; +use Alchemy\Zippy\Parser\ParserInterface; +use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; +use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException; + +/** + * ZipAdapter allows you to create and extract files from archives using Zip + * + * @see http://www.gnu.org/software/tar/manual/tar.html + */ +class ZipAdapter extends AbstractBinaryAdapter +{ + public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) + { + parent::__construct($parser, $manager, $inflator, $deflator); + $this->probe = new ZipVersionProbe($inflator, $deflator); + } + + /** + * @inheritdoc + */ + protected function doCreate($path, $files, $recursive) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + if (0 === count($files)) { + throw new NotSupportedException('Can not create empty zip archive'); + } + + if ($recursive) { + $builder->add('-r'); + } + + $builder->add($path); + + $collection = $this->manager->handle(getcwd(), $files); + $builder->setWorkingDirectory($collection->getContext()); + + $collection->forAll(function ($i, Resource $resource) use ($builder) { + return $builder->add($resource->getTarget()); + }); + + $process = $builder->getProcess(); + + try { + $process->run(); + } catch (ProcessException $e) { + $this->manager->cleanup($collection); + throw $e; + } + + $this->manager->cleanup($collection); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return new Archive($this->createResource($path), $this, $this->manager); + } + + /** + * @inheritdoc + */ + protected function doListMembers(ResourceInterface $resource) + { + $process = $this + ->deflator + ->create() + ->add('-l') + ->add($resource->getResource()) + ->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + $members = array(); + + foreach ($this->parser->parseFileListing($process->getOutput() ?: '') as $member) { + $members[] = new Member( + $resource, + $this, + $member['location'], + $member['size'], + $member['mtime'], + $member['is_dir'] + ); + } + + return $members; + } + + /** + * @inheritdoc + */ + protected function doAdd(ResourceInterface $resource, $files, $recursive) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + if ($recursive) { + $builder->add('-r'); + } + + $builder + ->add('-u') + ->add($resource->getResource()); + + $collection = $this->manager->handle(getcwd(), $files); + + $builder->setWorkingDirectory($collection->getContext()); + + $collection->forAll(function ($i, Resource $resource) use ($builder) { + return $builder->add($resource->getTarget()); + }); + + $process = $builder->getProcess(); + + try { + $process->run(); + } catch (ProcessException $e) { + $this->manager->cleanup($collection); + throw $e; + } + + $this->manager->cleanup($collection); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + } + + /** + * @inheritdoc + */ + protected function doGetDeflatorVersion() + { + $process = $this + ->deflator + ->create() + ->add('-h') + ->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $this->parser->parseDeflatorVersion($process->getOutput() ?: ''); + } + + /** + * @inheritdoc + */ + protected function doGetInflatorVersion() + { + $process = $this + ->inflator + ->create() + ->add('-h') + ->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $this->parser->parseInflatorVersion($process->getOutput() ?: ''); + } + + /** + * @inheritdoc + */ + protected function doRemove(ResourceInterface $resource, $files) + { + $files = (array) $files; + + $builder = $this + ->inflator + ->create(); + + $builder + ->add('-d') + ->add($resource->getResource()); + + if (!$this->addBuilderFileArgument($files, $builder)) { + throw new InvalidArgumentException('Invalid files'); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $files; + } + + /** + * @inheritdoc + */ + public static function getName() + { + return 'zip'; + } + + /** + * @inheritdoc + */ + public static function getDefaultDeflatorBinaryName() + { + return array('unzip'); + } + + /** + * @inheritdoc + */ + public static function getDefaultInflatorBinaryName() + { + return array('zip'); + } + + /** + * @inheritdoc + */ + protected function doExtract(ResourceInterface $resource, $to) + { + if (null !== $to && !is_dir($to)) { + throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); + } + + $builder = $this + ->deflator + ->create(); + + $builder + ->add('-o') + ->add($resource->getResource()); + + if (null !== $to) { + $builder + ->add('-d') + ->add($to); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return new \SplFileInfo($to ?: $resource->getResource()); + } + + /** + * @inheritdoc + */ + protected function doExtractMembers(ResourceInterface $resource, $members, $to) + { + if (null !== $to && !is_dir($to)) { + throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); + } + + $members = (array) $members; + + $builder = $this + ->deflator + ->create(); + + $builder + ->add($resource->getResource()); + + if (null !== $to) { + $builder + ->add('-d') + ->add($to); + } + + if (!$this->addBuilderFileArgument($members, $builder)) { + throw new InvalidArgumentException('Invalid files'); + } + + $process = $builder->getProcess(); + + $process->run(); + + if (!$process->isSuccessful()) { + throw new RuntimeException(sprintf( + 'Unable to execute the following command %s {output: %s}', + $process->getCommandLine(), + $process->getErrorOutput() + )); + } + + return $members; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php new file mode 100644 index 000000000..2d89606aa --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php @@ -0,0 +1,339 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Adapter; + +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Exception\NotSupportedException; +use Alchemy\Zippy\Archive\Member; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; +use Alchemy\Zippy\Adapter\Resource\ZipArchiveResource; +use Alchemy\Zippy\Adapter\VersionProbe\ZipExtensionVersionProbe; +use Alchemy\Zippy\Archive\Archive; +use Alchemy\Zippy\Resource\ResourceManager; +use Alchemy\Zippy\Resource\Resource; + +/** + * ZipExtensionAdapter allows you to create and extract files from archives + * using PHP Zip extension + * + * @see http://www.php.net/manual/en/book.zip.php + */ +class ZipExtensionAdapter extends AbstractAdapter +{ + private $errorCodesMapping = array( + \ZipArchive::ER_EXISTS => "File already exists", + \ZipArchive::ER_INCONS => "Zip archive inconsistent", + \ZipArchive::ER_INVAL => "Invalid argument", + \ZipArchive::ER_MEMORY => "Malloc failure", + \ZipArchive::ER_NOENT => "No such file", + \ZipArchive::ER_NOZIP => "Not a zip archive", + \ZipArchive::ER_OPEN => "Can't open file", + \ZipArchive::ER_READ => "Read error", + \ZipArchive::ER_SEEK => "Seek error" + ); + + public function __construct(ResourceManager $manager) + { + parent::__construct($manager); + $this->probe = new ZipExtensionVersionProbe(); + } + + /** + * @inheritdoc + */ + protected function doListMembers(ResourceInterface $resource) + { + $members = array(); + for ($i = 0; $i < $resource->getResource()->numFiles; $i++) { + $stat = $resource->getResource()->statIndex($i); + $members[] = new Member( + $resource, + $this, + $stat['name'], + $stat['size'], + new \DateTime('@' . $stat['mtime']), + 0 === strlen($resource->getResource()->getFromIndex($i, 1)) + ); + } + + return $members; + } + + /** + * @inheritdoc + */ + public static function getName() + { + return 'zip-extension'; + } + + /** + * @inheritdoc + */ + protected function doExtract(ResourceInterface $resource, $to) + { + return $this->extractMembers($resource, null, $to); + } + + /** + * @inheritdoc + */ + protected function doExtractMembers(ResourceInterface $resource, $members, $to) + { + if (null === $to) { + // if no destination is given, will extract to zip current folder + $to = dirname(realpath($resource->getResource()->filename)); + } + if (!is_dir($to)) { + $resource->getResource()->close(); + throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); + } + if (!is_writable($to)) { + $resource->getResource()->close(); + throw new InvalidArgumentException(sprintf("%s is not writable", $to)); + } + if (null !== $members) { + $membersTemp = (array) $members; + if (empty($membersTemp)) { + $resource->getResource()->close(); + + throw new InvalidArgumentException("no members provided"); + } + $members = array(); + // allows $members to be an array of strings or array of Members + foreach ($membersTemp as $member) { + if ($member instanceof Member) { + $member = $member->getLocation(); + } + if ($resource->getResource()->locateName($member) === false) { + $resource->getResource()->close(); + + throw new InvalidArgumentException(sprintf('%s is not in the zip file', $member)); + } + $members[] = $member; + } + } + + if (!$resource->getResource()->extractTo($to, $members)) { + $resource->getResource()->close(); + + throw new InvalidArgumentException(sprintf('Unable to extract archive : %s', $resource->getResource()->getStatusString())); + } + + return new \SplFileInfo($to); + } + + /** + * @inheritdoc + */ + protected function doRemove(ResourceInterface $resource, $files) + { + $files = (array) $files; + + if (empty($files)) { + throw new InvalidArgumentException("no files provided"); + } + + // either remove all files or none in case of error + foreach ($files as $file) { + if ($resource->getResource()->locateName($file) === false) { + $resource->getResource()->unchangeAll(); + $resource->getResource()->close(); + + throw new InvalidArgumentException(sprintf('%s is not in the zip file', $file)); + } + if (!$resource->getResource()->deleteName($file)) { + $resource->getResource()->unchangeAll(); + $resource->getResource()->close(); + + throw new RuntimeException(sprintf('unable to remove %s', $file)); + } + } + $this->flush($resource->getResource()); + + return $files; + } + + /** + * @inheritdoc + */ + protected function doAdd(ResourceInterface $resource, $files, $recursive) + { + $files = (array) $files; + if (empty($files)) { + $resource->getResource()->close(); + throw new InvalidArgumentException("no files provided"); + } + $this->addEntries($resource, $files, $recursive); + + return $files; + } + + /** + * @inheritdoc + */ + protected function doCreate($path, $files, $recursive) + { + $files = (array) $files; + + if (empty($files)) { + throw new NotSupportedException("Cannot create an empty zip"); + } + + $resource = $this->getResource($path, \ZipArchive::CREATE); + $this->addEntries($resource, $files, $recursive); + + return new Archive($resource, $this, $this->manager); + } + + /** + * Returns a new instance of the invoked adapter + * + * @return AbstractAdapter + * + * @throws RuntimeException In case object could not be instanciated + */ + public static function newInstance() + { + return new ZipExtensionAdapter(ResourceManager::create()); + } + + protected function createResource($path) + { + return $this->getResource($path, \ZipArchive::CHECKCONS); + } + + private function getResource($path, $mode) + { + $zip = new \ZipArchive(); + $res = $zip->open($path, $mode); + + if ($res !== true) { + throw new RuntimeException($this->errorCodesMapping[$res]); + } + + return new ZipArchiveResource($zip); + } + + private function addEntries(ZipArchiveResource $zipresource, array $files, $recursive) + { + $stack = new \SplStack(); + + $error = null; + $cwd = getcwd(); + $collection = $this->manager->handle($cwd, $files); + + $this->chdir($collection->getContext()); + + $adapter = $this; + + try { + $collection->forAll(function ($i, Resource $resource) use ($zipresource, $stack, $recursive, $adapter) { + $adapter->checkReadability($zipresource->getResource(), $resource->getTarget()); + if (is_dir($resource->getTarget())) { + if ($recursive) { + $stack->push($resource->getTarget() . ((substr($resource->getTarget(), -1) === DIRECTORY_SEPARATOR) ? '' : DIRECTORY_SEPARATOR )); + } else { + $adapter->addEmptyDir($zipresource->getResource(), $resource->getTarget()); + } + } else { + $adapter->addFileToZip($zipresource->getResource(), $resource->getTarget()); + } + + return true; + }); + + // recursively add dirs + while (!$stack->isEmpty()) { + $dir = $stack->pop(); + // removes . and .. + $files = array_diff(scandir($dir), array(".", "..")); + if (count($files) > 0) { + foreach ($files as $file) { + $file = $dir . $file; + $this->checkReadability($zipresource->getResource(), $file); + if (is_dir($file)) { + $stack->push($file . DIRECTORY_SEPARATOR); + } else { + $this->addFileToZip($zipresource->getResource(), $file); + } + } + } else { + $this->addEmptyDir($zipresource->getResource(), $dir); + } + } + $this->flush($zipresource->getResource()); + + $this->manager->cleanup($collection); + } catch (\Exception $e) { + $error = $e; + } + + $this->chdir($cwd); + + if ($error) { + throw $error; + } + } + + /** + * @info is public for PHP 5.3 compatibility, should be private + */ + public function checkReadability(\ZipArchive $zip, $file) + { + if (!is_readable($file)) { + $zip->unchangeAll(); + $zip->close(); + + throw new InvalidArgumentException(sprintf('could not read %s', $file)); + } + } + + /** + * @info is public for PHP 5.3 compatibility, should be private + */ + public function addFileToZip(\ZipArchive $zip, $file) + { + if (!$zip->addFile($file)) { + $zip->unchangeAll(); + $zip->close(); + + throw new RuntimeException(sprintf('unable to add %s to the zip file', $file)); + } + } + + /** + * @info is public for PHP 5.3 compatibility, should be private + */ + public function addEmptyDir(\ZipArchive $zip, $dir) + { + if (!$zip->addEmptyDir($dir)) { + $zip->unchangeAll(); + $zip->close(); + + throw new RuntimeException(sprintf('unable to add %s to the zip file', $dir)); + } + } + + /** + * Flushes changes to the archive + * + * @param \ZipArchive $zip + */ + private function flush(\ZipArchive $zip) // flush changes by reopening the file + { + $path = $zip->filename; + $zip->close(); + $zip->open($path, \ZipArchive::CHECKCONS); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php new file mode 100644 index 000000000..a1142bd09 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php @@ -0,0 +1,136 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Archive; + +use Alchemy\Zippy\Adapter\AdapterInterface; +use Alchemy\Zippy\Resource\ResourceManager; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; + +/** + * Represents an archive + */ +class Archive implements ArchiveInterface +{ + /** + * The path to the archive + * + * @var String + */ + protected $path; + + /** + * The archive adapter + * + * @var AdapterInterface + */ + protected $adapter; + + /** + * An array of archive members + * + * @var Array + */ + protected $members = array(); + + /** + * @var ResourceInterface + */ + protected $resource; + + /** + * + * @var ResourceManager + */ + protected $manager; + + /** + * Constructor + * + * @param ResourceInterface $resource Path to the archive + * @param AdapterInterface $adapter An archive adapter + * @param ResourceManager $manager The resource manager + */ + public function __construct(ResourceInterface $resource, AdapterInterface $adapter, ResourceManager $manager) + { + $this->resource = $resource; + $this->adapter = $adapter; + $this->manager = $manager; + } + + /** + * @inheritdoc + */ + public function count() + { + return count($this->getMembers()); + } + + /** + * Returns an Iterator for the current archive + * + * This method implements the IteratorAggregate interface. + * + * @return \ArrayIterator An iterator + */ + public function getIterator() + { + return new \ArrayIterator($this->getMembers()); + } + + /** + * @inheritdoc + */ + public function getMembers() + { + return $this->members = $this->adapter->listMembers($this->resource); + } + + /** + * @inheritdoc + */ + public function addMembers($sources, $recursive = true) + { + $this->adapter->add($this->resource, $sources, $recursive); + + return $this; + } + + /** + * @inheritdoc + */ + public function removeMembers($sources) + { + $this->adapter->remove($this->resource, $sources); + + return $this; + } + + /** + * @inheritdoc + */ + public function extract($toDirectory) + { + $this->adapter->extract($this->resource, $toDirectory); + + return $this; + } + + /** + * @inheritdoc + */ + public function extractMembers($members, $toDirectory = null) + { + $this->adapter->extractMembers($this->resource, $members, $toDirectory); + + return $this; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php new file mode 100644 index 000000000..fac5720a9 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Archive; + +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Exception\RuntimeException; + +interface ArchiveInterface extends \IteratorAggregate, \Countable +{ + /** + * Adds a file or a folder into the archive + * + * @param String|Array|\SplFileInfo $sources The path to the file or a folder + * @param Boolean $recursive Recurse into sub-directories + * + * @return ArchiveInterface + * + * @throws InvalidArgumentException In case the provided source path is not valid + * @throws RuntimeException In case of failure + */ + public function addMembers($sources, $recursive = true); + + /** + * Removes a file from the archive + * + * @param String|Array $sources The path to an archive or a folder member + * + * @return ArchiveInterface + * + * @throws RuntimeException In case of failure + */ + public function removeMembers($sources); + + /** + * Lists files and directories archive members + * + * @return Array An array of File + * + * @throws RuntimeException In case archive could not be read + */ + public function getMembers(); + + /** + * Extracts current archive to the given directory + * + * @param String $toDirectory The path the extracted archive + * + * @return ArchiveInterface + * + * @throws RuntimeException In case archive could not be extracted + */ + public function extract($toDirectory); + + /** + * Extracts specific members from the archive + * + * @param String|Array $members An array of members path + * @param string $toDirectory The destination $directory + * + * @return ArchiveInterface + * + * @throws RuntimeException In case member could not be extracted + */ + public function extractMembers($members, $toDirectory = null); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php new file mode 100644 index 000000000..c95190548 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Archive; + +use Alchemy\Zippy\Adapter\AdapterInterface; +use Alchemy\Zippy\Adapter\Resource\ResourceInterface; + +/** + * Represents a member of an archive. + */ +class Member implements MemberInterface +{ + /** + * The location of the file + * + * @var String + */ + private $location; + + /** + * Tells whether the archive member is a directory or not + * + * @var Boolean + */ + private $isDir; + + /** + * The uncompressed size of the file + * + * @var Integer + */ + private $size; + + /** + * The last modified date of the file + * + * @var \DateTime + */ + private $lastModifiedDate; + + /** + * The resource to the actual archive + * + * @var String + */ + private $resource; + + /** + * An adapter + * + * @var AdapterInterface + */ + private $adapter; + + /** + * Constructor + * + * @param ResourceInterface $resource The path of the archive which contain the member + * @param AdapterInterface $adapter The archive adapter interface + * @param String $location The path of the archive member + * @param Integer $fileSize The uncompressed file size + * @param \DateTime $lastModifiedDate The last modified date of the member + * @param Boolean $isDir Tells whether the member is a directory or not + */ + public function __construct(ResourceInterface $resource, AdapterInterface $adapter, $location, $fileSize, \DateTime $lastModifiedDate, $isDir) + { + $this->resource = $resource; + $this->adapter = $adapter; + $this->location = $location; + $this->isDir = $isDir; + $this->size = $fileSize; + $this->lastModifiedDate = $lastModifiedDate; + } + + /** + * @inheritdoc + */ + public function getLocation() + { + return $this->location; + } + + /** + * @inheritdoc + */ + public function isDir() + { + return $this->isDir; + } + + /** + * @inheritdoc + */ + public function getLastModifiedDate() + { + return $this->lastModifiedDate; + } + + /** + * @inheritdoc + */ + public function getSize() + { + return $this->size; + } + + /** + * @inheritdoc + */ + public function __toString() + { + return $this->location; + } + + /** + * {@inheritdoc} + */ + public function extract($to = null) + { + $this->adapter->extractMembers($this->resource, $this->location, $to); + + return new \SplFileInfo(sprintf('%s%s', rtrim(null === $to ? getcwd() : $to, '/'), $this->location)); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php new file mode 100644 index 000000000..786b155c5 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Archive; + +interface MemberInterface +{ + /** + * Gets the location of an archive member + * + * @return String + */ + public function getLocation(); + + /** + * Tells whether the member is a directory or not + * + * @return Boolean + */ + public function isDir(); + + /* + * Returns the last modified date of the member + * + * @return \DateTime + */ + public function getLastModifiedDate(); + + /** + * Returns the (uncompressed) size of the member + * + * If the size is unknown, returns -1 + * + * @return Integer + */ + public function getSize(); + + /** + * Extract the member from its archive + * + * Be careful using this method within a loop + * This will execute one extraction process for each file + * Prefer the use of ArchiveInterface::extractMembers in that use case + * + * @param String|null $to The path where to extract the member, if no path is not provided the member is extracted in the same directory of its archive + * + * @return \SplFileInfo The extracted file + * + * @throws RuntimeException In case of failure + * @throws InvalidArgumentException In case no members could be removed or provide extract target directory is not valid + */ + public function extract($to = null); + + /** + * @inheritdoc + */ + public function __toString(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php new file mode 100644 index 000000000..832d2eb6c --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +interface ExceptionInterface +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php new file mode 100644 index 000000000..681ac739f --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class FormatNotSupportedException extends RuntimeException +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php new file mode 100644 index 000000000..6da5dd211 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class IOException extends RuntimeException +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..c901135fa --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php new file mode 100644 index 000000000..40f863651 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class NoAdapterOnPlatformException extends RuntimeException +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php new file mode 100644 index 000000000..3d2626e04 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class NotSupportedException extends RuntimeException implements ExceptionInterface +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php new file mode 100644 index 000000000..a6aa51740 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php new file mode 100644 index 000000000..720c9e78a --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Exception; + +class TargetLocatorException extends RuntimeException +{ + private $resource; + + public function __construct($resource, $message, $code = 0, $previous = null) + { + $this->resource = $resource; + parent::__construct($message, $code, $previous); + } + + public function getResource() + { + return $this->resource; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php new file mode 100644 index 000000000..7d6cf209e --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +interface FileStrategyInterface +{ + /** + * Returns an array of adapters that match the strategy + * + * @return array + */ + public function getAdapters(); + + /** + * Returns the file extension that match the strategy + * + * @return string + */ + public function getFileExtension(); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php new file mode 100644 index 000000000..b63562bd9 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TB2FileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tb2'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php new file mode 100644 index 000000000..c3295b8ff --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TBz2FileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tbz2'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php new file mode 100644 index 000000000..7ace0a399 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TGzFileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tgz'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php new file mode 100644 index 000000000..13ab29531 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TarBz2FileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tar.bz2'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php new file mode 100644 index 000000000..930ea45e8 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TarFileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tar'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php new file mode 100644 index 000000000..5dae40b32 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class TarGzFileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'tar.gz'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php new file mode 100644 index 000000000..312bfb57a --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\FileStrategy; + +use Alchemy\Zippy\Adapter\AdapterContainer; + +class ZipFileStrategy implements FileStrategyInterface +{ + private $container; + + public function __construct(AdapterContainer $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function getAdapters() + { + return array( + $this->container['Alchemy\\Zippy\\Adapter\\ZipAdapter'], + $this->container['Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'], + ); + } + + /** + * {@inheritdoc} + */ + public function getFileExtension() + { + return 'zip'; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php new file mode 100644 index 000000000..364546ed3 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Alchemy\Zippy\Parser; + +use Alchemy\Zippy\Exception\RuntimeException; + +/** + * This class is responsible of parsing GNUTar command line output + */ +class BSDTarOutputParser implements ParserInterface +{ + const PERMISSIONS = "([ldrwx-]+)"; + const HARD_LINK = "(\d+)"; + const OWNER = "([a-z][-a-z0-9]*)"; + const GROUP = "([a-z][-a-z0-9]*)"; + const FILESIZE = "(\d*)"; + const DATE = "([a-zA-Z0-9]+\s+[a-z0-9]+\s+[a-z0-9:]+)"; + const FILENAME = "(.*)"; + + /** + * @inheritdoc + */ + public function parseFileListing($output) + { + $lines = array_values(array_filter(explode("\n", $output))); + $members = array(); + + // BSDTar outputs two differents format of date according to the mtime + // of the member. If the member is younger than six months the year is not shown. + // On 4.5+ FreeBSD system the day is displayed first + $dateFormats = array('M d Y', 'M d H:i', 'd M Y', 'd M H:i'); + + foreach ($lines as $line) { + $matches = array(); + + // drw-rw-r-- 0 toto titi 0 Jan 3 1980 practice/ + // -rw-rw-r-- 0 toto titi 10240 Jan 22 13:31 practice/records + if (!preg_match_all("#" . + self::PERMISSIONS . "\s+" . // match (drw-r--r--) + self::HARD_LINK . "\s+" . // match (1) + self::OWNER . "\s" . // match (toto) + self::GROUP . "\s+" . // match (titi) + self::FILESIZE . "\s+" . // match (0) + self::DATE . "\s+" . // match (Jan 3 1980) + self::FILENAME . // match (practice) + "#", $line, $matches, PREG_SET_ORDER + )) { + continue; + } + + $chunks = array_shift($matches); + + if (8 !== count($chunks)) { + continue; + } + + $date = null; + + foreach ($dateFormats as $format) { + $date = \DateTime::createFromFormat($format, $chunks[6]); + + if (false === $date) { + continue; + } else { + break; + } + } + + if (false === $date) { + throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line)); + } + + $members[] = array( + 'location' => $chunks[7], + 'size' => $chunks[5], + 'mtime' => $date, + 'is_dir' => 'd' === $chunks[1][0] + ); + } + + return $members; + } + + /** + * @inheritdoc + */ + public function parseInflatorVersion($output) + { + $chunks = explode(' ', $output, 3); + + if (2 > count($chunks)) { + return null; + } + + list($name, $version) = explode(' ', $output, 3); + + return $version; + } + + /** + * @inheritdoc + */ + public function parseDeflatorVersion($output) + { + return $this->parseInflatoVersion($output); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php new file mode 100644 index 000000000..2cd7127f4 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Alchemy\Zippy\Parser; + +use Alchemy\Zippy\Exception\RuntimeException; + +/** + * This class is responsible of parsing GNUTar command line output + */ +class GNUTarOutputParser implements ParserInterface +{ + const PERMISSIONS = "([ldrwx-]+)"; + const OWNER = "([a-z][-a-z0-9]*)"; + const GROUP = "([a-z][-a-z0-9]*)"; + const FILESIZE = "(\d*)"; + const ISO_DATE = "([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)"; + const FILENAME = "(.*)"; + + /** + * @inheritdoc + */ + public function parseFileListing($output) + { + $lines = array_values(array_filter(explode("\n", $output))); + $members = array(); + + foreach ($lines as $line) { + $matches = array(); + + // -rw-r--r-- gray/staff 62373 2006-06-09 12:06 apple + if (!preg_match_all("#". + self::PERMISSIONS . "\s+" . // match (-rw-r--r--) + self::OWNER . "/" . // match (gray) + self::GROUP . "\s+" . // match (staff) + self::FILESIZE . "\s+" . // match (62373) + self::ISO_DATE . "\s+" . // match (2006-06-09 12:06) + self::FILENAME . // match (apple) + "#", + $line, $matches, PREG_SET_ORDER + )) { + continue; + } + + $chunks = array_shift($matches); + + if (7 !== count($chunks)) { + continue; + } + + $date = \DateTime::createFromFormat("Y-m-d H:i", $chunks[5]); + + if (false === $date) { + throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line)); + } + + $members[] = array( + 'location' => $chunks[6], + 'size' => $chunks[4], + 'mtime' => $date, + 'is_dir' => 'd' === $chunks[1][0] + ); + } + + return $members; + } + + /** + * @inheritdoc + */ + public function parseInflatorVersion($output) + { + $chunks = explode(' ', $output, 3); + + if (2 > count($chunks)) { + return null; + } + + list($name, $version) = $chunks; + + return $version; + } + + /** + * @inheritdoc + */ + public function parseDeflatorVersion($output) + { + return $this->parseInflatoVersion($output); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php new file mode 100644 index 000000000..c515716b9 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Parser; + +use Alchemy\Zippy\Exception\InvalidArgumentException; + +class ParserFactory +{ + /** + * Maps the corresponding parser to the selected adapter + * + * @param $adapterName An adapter name + * + * @return ParserInterface + * + * @throws InvalidArgumentException In case no parser were found + */ + public static function create($adapterName) + { + switch ($adapterName) { + case 'gnu-tar': + return new GNUTarOutputParser(); + break; + case 'bsd-tar': + return new BSDTarOutputParser(); + break; + case 'zip': + return new ZipOutputParser(); + break; + + default: + throw new InvalidArgumentException(sprintf('No parser available for %s adapter', $adapterName)); + break; + } + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php new file mode 100644 index 000000000..3e25e6c8f --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Parser; + +use Alchemy\Zippy\Exception\RuntimeException; + +interface ParserInterface +{ + /** + * Parses a file listing + * + * @param String $output The string to parse + * + * @return Array An array of Member properties (location, mtime, size & is_dir) + * + * @throws RuntimeException In case the parsing process failed + */ + public function parseFileListing($output); + + /** + * Parses the inflator binary version + * + * @param String $output + * + * @return String The version + */ + public function parseInflatorVersion($output); + + /** + * Parses the deflator binary version + * + * @param String $output + * + * @return String The version + */ + public function parsedeflatorVersion($output); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php new file mode 100644 index 000000000..507501a48 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Alchemy\Zippy\Parser; + +/** + * This class is responsible of parsing GNUTar command line output + */ +class ZipOutputParser implements ParserInterface +{ + const LENGTH = "(\d*)"; + const ISO_DATE = "([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)"; + const FILENAME = "(.*)"; + + /** + * @inheritdoc + */ + public function parseFileListing($output) + { + $lines = array_values(array_filter(explode("\n", $output))); + $members = array(); + + foreach ($lines as $line) { + $matches = array(); + + // 785 2012-10-24 10:39 file + if (!preg_match_all("#". + self::LENGTH . "\s+" . // match (785) + self::ISO_DATE . "\s+" . // match (2012-10-24 10:39) + self::FILENAME . // match (file) + "#", + $line, $matches, PREG_SET_ORDER + )) { + continue; + } + + $chunks = array_shift($matches); + + if (4 !== count($chunks)) { + continue; + } + + $members[] = array( + 'location' => $chunks[3], + 'size' => $chunks[1], + 'mtime' => \DateTime::createFromFormat("Y-m-d H:i", $chunks[2]), + 'is_dir' => '/' === substr($chunks[3], -1) + ); + } + + return $members; + } + + /** + * @inheritdoc + */ + public function parseInflatorVersion($output) + { + $lines = array_values(array_filter(explode("\n", $output, 3))); + + $chunks = explode(' ', $lines[1], 3); + + if (2 > count($chunks)) { + return null; + } + + list($name, $version) = $chunks; + + return $version; + } + + /** + * @inheritdoc + */ + public function parseDeflatorVersion($output) + { + $lines = array_values(array_filter(explode("\n", $output, 2))); + $firstLine = array_shift($lines); + $chunks = explode(' ', $firstLine, 3); + + if (2 > count($chunks)) { + return null; + } + + list($name, $version) = $chunks; + + return $version; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php new file mode 100644 index 000000000..f6aa18a5f --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\ProcessBuilder; + +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Symfony\Component\Process\ProcessBuilder; + +class ProcessBuilderFactory implements ProcessBuilderFactoryInterface +{ + /** + * The binary path + * + * @var String + */ + protected $binary; + + /** + * Constructor + * + * @param String $binary The path to the binary + * + * @throws InvalidArgumentException In case binary path is invalid + */ + public function __construct($binary) + { + $this->useBinary($binary); + } + + /** + * @inheritdoc + */ + public function getBinary() + { + return $this->binary; + } + + /** + * @inheritdoc + */ + public function useBinary($binary) + { + if (!is_executable($binary)) { + throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary)); + } + + $this->binary = $binary; + + return $this; + } + + /** + * @inheritdoc + */ + public function create() + { + if (null === $this->binary) { + throw new InvalidArgumentException('No binary set'); + } + + return ProcessBuilder::create(array($this->binary))->setTimeout(null); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php new file mode 100644 index 000000000..f8149a9a4 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\ProcessBuilder; + +use Symfony\Component\Process\ProcessBuilder; +use Alchemy\Zippy\Exception\InvalidArgumentException; + +interface ProcessBuilderFactoryInterface +{ + /** + * Returns a new instance of Symfony ProcessBuilder + * + * @return ProcessBuilder + * + * @throws InvalidArgumentException + */ + public function create(); + + /** + * Returns the binary path + * + * @return String + */ + public function getBinary(); + + /** + * Sets the binary path + * + * @param String $binary A binary path + * + * @return ProcessBuilderFactoryInterface + * + * @throws InvalidArgumentException In case binary is not executable + */ + public function useBinary($binary); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php new file mode 100644 index 000000000..9669089c7 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +class RequestMapper +{ + private $locator; + + /** + * Constructor + * + * @param TargetLocator $locator + */ + public function __construct(TargetLocator $locator) + { + $this->locator = $locator; + } + + /** + * Maps resources request to a ResourceCollection + * + * @return ResourceCollection + */ + public function map($context, array $resources) + { + $data = array(); + + foreach ($resources as $location => $resource) { + if (is_int($location)) { + $data[] = new Resource($resource, $this->locator->locate($context, $resource)); + } else { + $data[] = new Resource($resource, ltrim($location, '/')); + } + } + + if (count($data) === 1) { + $context = $data[0]->getOriginal(); + } + + $collection = new ResourceCollection($context, $data, false); + + return $collection; + } + + /** + * Creates the default RequestMapper + * + * @return RequestMapper + */ + public static function create() + { + return new static(new TargetLocator()); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php new file mode 100644 index 000000000..11b0ae4e0 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +class Resource +{ + private $original; + private $target; + + /** + * Constructor + * + * @param String $original + * @param String $target + */ + public function __construct($original, $target) + { + $this->original = $original; + $this->target = $target; + } + + /** + * Returns the target + * + * @return String + */ + public function getTarget() + { + return $this->target; + } + + /** + * Returns the original path + * + * @return String + */ + public function getOriginal() + { + return $this->original; + } + + /** + * Returns whether the resource can be processed in place given a context or not. + * + * For example : + * - /path/to/file1 can be processed to file1 in /path/to context + * - /path/to/subdir/file2 can be processed to subdir/file2 in /path/to context + * + * @param String $context + * + * @return Boolean + */ + public function canBeProcessedInPlace($context) + { + if (!is_string($this->original)) { + return false; + } + + if (!$this->isLocal()) { + return false; + } + + $data = parse_url($this->original); + + return sprintf('%s/%s', rtrim($context, '/'), $this->target) === $data['path']; + } + + /** + * Returns a context for computing this resource in case it is possible. + * + * Useful to avoid teleporting. + * + * @return null|string + */ + public function getContextForProcessInSinglePlace() + { + if (!$this->isLocal()) { + return null; + } + + if (basename($this->original) === $this->target) { + return dirname($this->original); + } + } + + /** + * Returns true if the resource is local. + * + * @return Boolean + */ + private function isLocal() + { + $data = parse_url($this->original); + + return isset($data['path']); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php new file mode 100644 index 000000000..ab69044d1 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Doctrine\Common\Collections\ArrayCollection; + +class ResourceCollection extends ArrayCollection +{ + private $context; + private $temporary; + + /** + * Constructor + * + * @param String $context + * @param Resource[] $elements An array of Resource + */ + public function __construct($context, array $elements, $temporary) + { + array_walk($elements, function ($element) { + if (!$element instanceof Resource) { + throw new InvalidArgumentException('ResourceCollection only accept Resource elements'); + } + }); + + $this->context = $context; + $this->temporary = (Boolean) $temporary; + parent::__construct($elements); + } + + /** + * Returns the context related to the collection + * + * @return String + */ + public function getContext() + { + return $this->context; + } + + /** + * Tells whether the collection is temporary or not. + * + * A ResourceCollection is temporary when it required a temporary folder to + * fetch data + * + * @return type + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Returns true if all resources can be processed in place, false otherwise + * + * @return Boolean + */ + public function canBeProcessedInPlace() + { + if (count($this) === 1) { + if (null !== $context = $this->first()->getContextForProcessInSinglePlace()) { + $this->context = $context; + return true; + } + } + + foreach ($this as $resource) { + if (!$resource->canBeProcessedInPlace($this->context)) { + return false; + } + } + + return true; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php new file mode 100644 index 000000000..be93ef4ce --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +use Alchemy\Zippy\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Filesystem\Exception\IOException as SfIOException; + +class ResourceManager +{ + private $mapper; + private $teleporter; + private $filesystem; + + /** + * Constructor + * + * @param RequestMapper $mapper + * @param ResourceTeleporter $teleporter + * @param Filesystem $filesystem + */ + public function __construct(RequestMapper $mapper, ResourceTeleporter $teleporter, Filesystem $filesystem) + { + $this->mapper = $mapper; + $this->filesystem = $filesystem; + $this->teleporter = $teleporter; + } + + /** + * Handles an archival request. + * + * The request is an array of string|streams to compute in a context (current + * working directory most of the time) + * Some keys can be associative. In these cases, the key is used the target + * for the file. + * + * @param String $context + * @param Array $request + * + * @return ResourceCollection + * + * @throws IOException In case of write failure + */ + public function handle($context, array $request) + { + $collection = $this->mapper->map($context, $request); + + if (!$collection->canBeProcessedInPlace()) { + $context = sprintf('%s/%s', sys_get_temp_dir(), uniqid('zippy_')); + + try { + $this->filesystem->mkdir($context); + } catch (SfIOException $e) { + throw new IOException(sprintf('Could not create temporary folder %s', $context), $e->getCode(), $e); + } + + foreach ($collection as $resource) { + $this->teleporter->teleport($context, $resource); + } + + $collection = new ResourceCollection($context, $collection->toArray(), true); + } + + return $collection; + } + + /** + * This method must be called once the ResourceCollection has been processed. + * + * It will remove temporary files + * + * @todo this should be done in the __destruct method of ResourceCollection + * + * @param ResourceCollection $collection + */ + public function cleanup(ResourceCollection $collection) + { + if ($collection->isTemporary()) { + try { + $this->filesystem->remove($collection->getContext()); + } catch (IOException $e) { + // log this ? + } + } + } + + /** + * Creates a default ResourceManager + * + * @return ResourceManager + */ + public static function create() + { + return new static(RequestMapper::create(), ResourceTeleporter::create(), new Filesystem()); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php new file mode 100644 index 000000000..362a85187 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +class ResourceTeleporter +{ + private $container; + + /** + * Constructor + * + * @param TeleporterContainer $container + */ + public function __construct(TeleporterContainer $container) + { + $this->container = $container; + } + + /** + * Teleports a Resource to its target in the context + * + * @param String $context + * @param Resource $resource + * + * @return ResourceTeleporter + */ + public function teleport($context, Resource $resource) + { + $this + ->container + ->fromResource($resource) + ->teleport($resource, $context); + + return $this; + } + + /** + * Creates the ResourceTeleporter with the default TeleporterContainer + * + * @return ResourceTeleporter + */ + public static function create() + { + return new static(TeleporterContainer::load()); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php new file mode 100644 index 000000000..ed8e2df10 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php @@ -0,0 +1,156 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +use Alchemy\Zippy\Exception\TargetLocatorException; + +class TargetLocator +{ + /** + * Locates the target for a resource in a context + * + * For example, adding /path/to/file where the context (current working + * directory) is /path/to will return `file` as target + * + * @param String $context + * @param String|resource $resource + * + * @return String + * + * @throws TargetLocatorException In case the resource is invalid + */ + public function locate($context, $resource) + { + switch (true) { + case is_resource($resource): + return $this->locateResource($resource); + case is_string($resource): + return $this->locateString($context, $resource); + case $resource instanceof \SplFileInfo: + return $this->locateString($context, $resource->getRealpath()); + default: + throw new TargetLocatorException($resource, 'Unknown resource format'); + } + } + + /** + * Locate the target for a resource. + * + * @param resource $resource + * + * @return String + * + * @throws TargetLocatorException + */ + private function locateResource($resource) + { + $meta = stream_get_meta_data($resource); + $data = parse_url($meta['uri']); + + if (!isset($data['path'])) { + throw new TargetLocatorException($resource, 'Unable to retrieve path from resource'); + } + + return basename($data['path']); + } + + /** + * Locate the target for a string. + * + * @param String $resource + * + * @return String + * + * @throws TargetLocatorException + */ + private function locateString($context, $resource) + { + $url = parse_url($resource); + + if (isset($url['scheme']) && $this->isLocalFilesystem($url['scheme'])) { + $resource = $url['path'] = $this->cleanupPath($url['path']); + } + + // resource is a URI + if (isset($url['scheme'])) { + if ($this->isLocalFilesystem($url['scheme']) && $this->isFileInContext($url['path'], $context)) { + return $this->getRelativePathFromContext($url['path'], $context); + } + + return basename($resource); + } + + // resource is a local path + if ($this->isFileInContext($resource, $context)) { + $resource = $this->cleanupPath($resource); + + return $this->getRelativePathFromContext($resource, $context); + } else { + return basename($resource); + } + } + + /** + * Removes backward path sequences (..) + * + * @param String $path + * + * @return String + * + * @throws TargetLocatorException In case the path is invalid + */ + private function cleanupPath($path) + { + if (false === $cleanPath = realpath($path)) { + throw new TargetLocatorException($path, sprintf('%s is an invalid location', $path)); + } + + return $cleanPath; + } + + /** + * Checks whether the path belong to the context + * + * @param String $path A resource path + * @param String $context + * + * @return Boolean + */ + private function isFileInContext($path, $context) + { + return 0 === strpos($path, $context); + } + + /** + * Gets the relative path from the context for the given path + * + * @param String $path A resource path + * + * @return String + */ + private function getRelativePathFromContext($path, $context) + { + return ltrim(str_replace($context, '', $path), '/\\'); + } + + /** + * Checks if a scheme reffers to a local filesystem + * + * @param String $scheme + * + * @return Boolean + */ + private function isLocalFilesystem($scheme) + { + return 'plainfile' === $scheme || 'file' === $scheme; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php new file mode 100644 index 000000000..ee129ec16 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource\Teleporter; + +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Exception\IOException; + +abstract class AbstractTeleporter implements TeleporterInterface +{ + /** + * Writes the target + * + * @param String $data + * @param Resource $resource + * @param String $context + * + * @return TeleporterInterface + * + * @throws IOException + */ + protected function writeTarget($data, Resource $resource, $context) + { + $target = $this->getTarget($context, $resource); + + if (false === file_put_contents($target, $data)) { + throw new IOException(sprintf('Could not write to %s', $target)); + } + + return $this; + } + + /** + * Returns the relative target of a Resource + * + * @param String $context + * @param Resource $resource + * + * @return String + */ + protected function getTarget($context, Resource $resource) + { + return sprintf('%s/%s', rtrim($context, '/'), $resource->getTarget()); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php new file mode 100644 index 000000000..0133c0728 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource\Teleporter; + +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Exception\RuntimeException; +use Guzzle\Http\Client; +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Common\Event; + +/** + * Guzzle Teleporter implementation for HTTP resources + */ +class GuzzleTeleporter extends AbstractTeleporter +{ + private $client; + + /** + * Constructor + * + * @param Client $client + */ + public function __construct(Client $client) + { + $this->client = $client; + } + + /** + * {@inheritdoc} + */ + public function teleport(Resource $resource, $context) + { + $target = $this->getTarget($context, $resource); + + $stream = fopen($target, 'w'); + $response = $this->client->get($resource->getOriginal(), null, $stream)->send(); + fclose($stream); + + if (!$response->isSuccessful()) { + throw new RuntimeException(sprintf('Unable to fetch %s', $resource->getOriginal())); + } + + return $this; + } + + /** + * Creates the GuzzleTeleporter + * + * @return GuzzleTeleporter + */ + public static function create() + { + $client = new Client(); + + $client->getEventDispatcher()->addListener('request.error', function (Event $event) { + // override guzzle default behavior of throwing exceptions + // when 4xx & 5xx responses are encountered + $event->stopPropagation(); + }, -254); + + $client->addSubscriber(BackoffPlugin::getExponentialBackoff(5, array(500, 502, 503, 408))); + + return new static($client); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php new file mode 100644 index 000000000..89674ec83 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource\Teleporter; + +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Exception\IOException; +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Filesystem\Exception\IOException as SfIOException; + +/** + * This class transport an object using the local filesystem + */ +class LocalTeleporter extends AbstractTeleporter +{ + private $filesystem; + + /** + * Constructor + * + * @param Filesystem $filesystem + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + /** + * {@inheritdoc} + */ + public function teleport(Resource $resource, $context) + { + $target = $this->getTarget($context, $resource); + $path = $resource->getOriginal(); + + if (!file_exists($path)) { + throw new InvalidArgumentException(sprintf('Invalid path %s', $path)); + } + + try { + if (is_file($path)) { + $this->filesystem->copy($path, $target); + } elseif (is_dir($path)) { + $this->filesystem->mirror($path, $target); + } else { + throw new InvalidArgumentException(sprintf('Invalid file or directory %s', $path)); + } + } catch (SfIOException $e) { + throw new IOException(sprintf('Could not write %s', $target), $e->getCode(), $e); + } + } + + /** + * Creates the LocalTeleporter + * + * @return LocalTeleporter + */ + public static function create() + { + return new static(new Filesystem()); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php new file mode 100644 index 000000000..4150f4052 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource\Teleporter; + +use Alchemy\Zippy\Resource\Resource; +use Alchemy\Zippy\Exception\InvalidArgumentException; + +/** + * This class transport an object using php stream wrapper + */ +class StreamTeleporter extends AbstractTeleporter +{ + /** + * {@inheritdoc} + */ + public function teleport(Resource $resource, $context) + { + $streamCreated = false; + + if (is_resource($resource->getOriginal())) { + $stream = $resource->getOriginal(); + } else { + $stream = @fopen($resource->getOriginal(), 'rb'); + + if (!is_resource($stream)) { + throw new InvalidArgumentException(sprintf( + 'The stream or file "%s" could not be opened: ', + $resource->getOriginal() + )); + } + $streamCreated = true; + } + + $content = stream_get_contents($stream); + + if ($streamCreated) { + fclose($stream); + } + + $this->writeTarget($content, $resource, $context); + } + + /** + * Creates the StreamTeleporter + * + * @return StreamTeleporter + */ + public static function create() + { + return new static(); + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php new file mode 100644 index 000000000..6f4012be3 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource\Teleporter; + +use Alchemy\Zippy\Resource\Resource; + +interface TeleporterInterface +{ + /** + * Teleport a file from a destination to an other + * + * @param Resource $resource A Resource + * @param string $context The current context + * + * @throws IOException In case file could not be written on local + * @throws InvalidArgumentException In case path to file is not valid + */ + public function teleport(Resource $resource, $context); +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php new file mode 100644 index 000000000..3109a2497 --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy\Resource; + +use Alchemy\Zippy\Exception\InvalidArgumentException; +use Alchemy\Zippy\Resource\Teleporter\LocalTeleporter; +use Alchemy\Zippy\Resource\Teleporter\GuzzleTeleporter; +use Alchemy\Zippy\Resource\Teleporter\StreamTeleporter; +use Alchemy\Zippy\Resource\Teleporter\TeleporterInterface; + +/** + * A container of TeleporterInterface + */ +class TeleporterContainer extends \Pimple +{ + /** + * Returns the appropriate TeleporterInterface given a Resource + * + * @param Resource $resource + * + * @return TeleporterInterface + * + * @throws InvalidArgumentException + */ + public function fromResource(Resource $resource) + { + switch (true) { + case is_resource($resource->getOriginal()): + $teleporter = 'stream-teleporter'; + break; + case is_string($resource->getOriginal()): + $data = parse_url($resource->getOriginal()); + + if (!isset($data['scheme']) || 'file' === $data['scheme']) { + $teleporter = 'local-teleporter'; + } elseif (in_array($data['scheme'], array('http', 'https'))) { + $teleporter = 'guzzle-teleporter'; + } else { + $teleporter = 'stream-teleporter'; + } + break; + default: + throw new InvalidArgumentException('No teleporter found'); + } + + return $this[$teleporter]; + } + + /** + * Instantiates TeleporterContainer and register default teleporters + * + * @return TeleporterContainer + */ + public static function load() + { + $container = new static(); + + $container['stream-teleporter'] = $container->share(function () { + return StreamTeleporter::create(); + }); + $container['local-teleporter'] = $container->share(function () { + return LocalTeleporter::create(); + }); + $container['guzzle-teleporter'] = $container->share(function () { + return GuzzleTeleporter::create(); + }); + + return $container; + } +} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php new file mode 100644 index 000000000..60fb5969f --- /dev/null +++ b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Alchemy\Zippy; + +use Alchemy\Zippy\Adapter\AdapterInterface; +use Alchemy\Zippy\Adapter\AdapterContainer; +use Alchemy\Zippy\Archive\ArchiveInterface; +use Alchemy\Zippy\Exception\ExceptionInterface; +use Alchemy\Zippy\Exception\FormatNotSupportedException; +use Alchemy\Zippy\Exception\NoAdapterOnPlatformException; +use Alchemy\Zippy\Exception\RuntimeException; +use Alchemy\Zippy\FileStrategy\FileStrategyInterface; +use Alchemy\Zippy\FileStrategy\TarFileStrategy; +use Alchemy\Zippy\FileStrategy\TarBz2FileStrategy; +use Alchemy\Zippy\FileStrategy\TarGzFileStrategy; +use Alchemy\Zippy\FileStrategy\TB2FileStrategy; +use Alchemy\Zippy\FileStrategy\TBz2FileStrategy; +use Alchemy\Zippy\FileStrategy\TGzFileStrategy; +use Alchemy\Zippy\FileStrategy\ZipFileStrategy; + +class Zippy +{ + public $adapters; + private $strategies = array(); + + public function __construct(AdapterContainer $adapters) + { + $this->adapters = $adapters; + } + + /** + * Creates an archive + * + * @param string $path + * @param String|Array|\Traversable|null $files + * @param Boolean $recursive + * @param string|null $type + * + * @return ArchiveInterface + * + * @throws RuntimeException In case of failure + */ + public function create($path, $files = null, $recursive = true, $type = null) + { + if (null === $type) { + $type = $this->guessAdapterExtension($path); + } + + try { + return $this + ->getAdapterFor($this->sanitizeExtension($type)) + ->create($path, $files, $recursive); + } catch (ExceptionInterface $e) { + throw new RuntimeException('Unable to create archive', $e->getCode(), $e); + } + } + + /** + * Opens an archive. + * + * @param string $path + * + * @return ArchiveInterface + * + * @throws RuntimeException In case of failure + */ + public function open($path) + { + $type = $this->guessAdapterExtension($path); + + try { + return $this + ->getAdapterFor($this->sanitizeExtension($type)) + ->open($path); + } catch (ExceptionInterface $e) { + throw new RuntimeException('Unable to open archive', $e->getCode(), $e); + } + } + + /** + * Adds a strategy. + * + * The last strategy added is preferred over the other ones. + * You can add a strategy twice ; when doing this, the first one is removed + * when inserting the second one. + * + * @param FileStrategyInterface $strategy + * + * @return Zippy + */ + public function addStrategy(FileStrategyInterface $strategy) + { + $extension = $this->sanitizeExtension($strategy->getFileExtension()); + + if (!isset($this->strategies[$extension])) { + $this->strategies[$extension] = array(); + } + + if (false !== $key = array_search($strategy, $this->strategies[$extension], true)) { + unset($this->strategies[$extension][$key]); + } + + array_unshift($this->strategies[$extension], $strategy); + + return $this; + } + + /** + * Returns the strategies as they are stored + * + * @return array + */ + public function getStrategies() + { + return $this->strategies; + } + + /** + * Returns an adapter for a file extension + * + * @param string $extension The extension + * + * @return AdapterInterface + * + * @throws FormatNotSupportedException When no strategy is defined for this extension + * @throws NoAdapterOnPlatformException When no adapter is supported for this extension on this platform + */ + public function getAdapterFor($extension) + { + $extension = $this->sanitizeExtension($extension); + + if (!$extension || !isset($this->strategies[$extension])) { + throw new FormatNotSupportedException(sprintf('No strategy for %s extension', $extension)); + } + + foreach ($this->strategies[$extension] as $strategy) { + foreach ($strategy->getAdapters() as $adapter) { + if ($adapter->isSupported()) { + return $adapter; + } + } + } + + throw new NoAdapterOnPlatformException(sprintf('No adapter available for %s on this platform', $extension)); + } + + /** + * Creates Zippy and loads default strategies + * + * @return Zippy + */ + public static function load() + { + $adapters = AdapterContainer::load(); + $factory = new static($adapters); + + $factory->addStrategy(new ZipFileStrategy($adapters)); + $factory->addStrategy(new TarFileStrategy($adapters)); + $factory->addStrategy(new TarGzFileStrategy($adapters)); + $factory->addStrategy(new TarBz2FileStrategy($adapters)); + $factory->addStrategy(new TB2FileStrategy($adapters)); + $factory->addStrategy(new TBz2FileStrategy($adapters)); + $factory->addStrategy(new TGzFileStrategy($adapters)); + + return $factory; + } + + /** + * Sanitize an extension. + * + * Strips dot from the beginning, converts to lowercase and remove trailing + * whitespaces + * + * @param string $extension + * + * @return string + */ + private function sanitizeExtension($extension) + { + return ltrim(trim(mb_strtolower($extension)), '.'); + } + + /** + * Finds an extension that has strategy registered given a file path + * + * Returns null if no matching strategy found. + * + * @param string $path + * + * @return string|null + */ + private function guessAdapterExtension($path) + { + $path = strtolower(trim($path)); + + foreach ($this->strategies as $extension => $strategy) { + if ($extension === substr($path, (strlen($extension) * -1))) { + return $extension; + } + } + + return null; + } +} diff --git a/core/lib/doctrine/collections/.gitignore b/core/lib/doctrine/collections/.gitignore new file mode 100644 index 000000000..48b8bf907 --- /dev/null +++ b/core/lib/doctrine/collections/.gitignore @@ -0,0 +1 @@ +vendor/ diff --git a/core/lib/doctrine/collections/.travis.yml b/core/lib/doctrine/collections/.travis.yml new file mode 100644 index 000000000..470c9879a --- /dev/null +++ b/core/lib/doctrine/collections/.travis.yml @@ -0,0 +1,10 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - hhvm + +before_script: + - composer --prefer-source --dev install diff --git a/core/lib/doctrine/collections/LICENSE b/core/lib/doctrine/collections/LICENSE new file mode 100644 index 000000000..5e781fce4 --- /dev/null +++ b/core/lib/doctrine/collections/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/core/lib/doctrine/collections/README.md b/core/lib/doctrine/collections/README.md new file mode 100644 index 000000000..627d45bbb --- /dev/null +++ b/core/lib/doctrine/collections/README.md @@ -0,0 +1,17 @@ +# Doctrine Collections + +Collections Abstraction library + +## Changelog + +### v1.2 + +* Add a new ``AbstractLazyCollection`` + +### v1.1 + +* Deprecated ``Comparison::IS``, because it's only there for SQL semantics. + These are fixed in the ORM instead. +* Add ``Comparison::CONTAINS`` to perform partial string matches: + + $criteria->andWhere($criteria->expr()->contains('property', 'Foo')); diff --git a/core/lib/doctrine/collections/composer.json b/core/lib/doctrine/collections/composer.json new file mode 100644 index 000000000..dd30961c8 --- /dev/null +++ b/core/lib/doctrine/collections/composer.json @@ -0,0 +1,26 @@ +{ + "name": "doctrine/collections", + "type": "library", + "description": "Collections Abstraction library", + "keywords": ["collections", "array", "iterator"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php new file mode 100644 index 000000000..0052a29c7 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php @@ -0,0 +1,343 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure; + +/** + * Lazy collection that is backed by a concrete collection + * + * @author Michaël Gallego + * @since 1.2 + */ +abstract class AbstractLazyCollection implements Collection +{ + /** + * The backed collection to use + * + * @var Collection + */ + protected $collection; + + /** + * @var bool + */ + private $initialized = false; + + /** + * {@inheritDoc} + */ + public function count() + { + $this->initialize(); + return $this->collection->count(); + } + + /** + * {@inheritDoc} + */ + public function add($element) + { + $this->initialize(); + return $this->collection->add($element); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->initialize(); + $this->collection->clear(); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + $this->initialize(); + return $this->collection->contains($element); + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + $this->initialize(); + return $this->collection->isEmpty(); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + $this->initialize(); + return $this->collection->remove($key); + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $this->initialize(); + return $this->collection->removeElement($element); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + $this->initialize(); + return $this->collection->containsKey($key); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $this->initialize(); + return $this->collection->get($key); + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + $this->initialize(); + return $this->collection->getKeys(); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + $this->initialize(); + return $this->collection->getValues(); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->initialize(); + $this->collection->set($key, $value); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + $this->initialize(); + return $this->collection->toArray(); + } + + /** + * {@inheritDoc} + */ + public function first() + { + $this->initialize(); + return $this->collection->first(); + } + + /** + * {@inheritDoc} + */ + public function last() + { + $this->initialize(); + return $this->collection->last(); + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->initialize(); + return $this->collection->key(); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->initialize(); + return $this->collection->current(); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $this->initialize(); + return $this->collection->next(); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + return $this->collection->exists($p); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + return $this->collection->filter($p); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + return $this->collection->forAll($p); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + $this->initialize(); + return $this->collection->map($func); + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + return $this->collection->partition($p); + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + $this->initialize(); + return $this->collection->indexOf($element); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + $this->initialize(); + return $this->collection->slice($offset, $length); + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + $this->initialize(); + return $this->collection->getIterator(); + } + + /** + * {@inheritDoc} + */ + public function offsetExists($offset) + { + $this->initialize(); + return $this->collection->offsetExists($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetGet($offset) + { + $this->initialize(); + return $this->collection->offsetGet($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + $this->initialize(); + $this->collection->offsetSet($offset, $value); + } + + /** + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + $this->initialize(); + $this->collection->offsetUnset($offset); + } + + /** + * Is the lazy collection already initialized? + * + * @return bool + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * Initialize the collection + * + * @return void + */ + protected function initialize() + { + if (!$this->initialized) { + $this->doInitialize(); + $this->initialized = true; + } + } + + /** + * Do the initialization logic + * + * @return void + */ + abstract protected function doInitialize(); +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 000000000..9c2c8e147 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,385 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, ArrayIterator; +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; + +/** + * An ArrayCollection is a Collection implementation that wraps a regular PHP array. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArrayCollection implements Collection, Selectable +{ + /** + * An array containing the entries of this collection. + * + * @var array + */ + private $_elements; + + /** + * Initializes a new ArrayCollection. + * + * @param array $elements + */ + public function __construct(array $elements = array()) + { + $this->_elements = $elements; + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->_elements; + } + + /** + * {@inheritDoc} + */ + public function first() + { + return reset($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function last() + { + return end($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function next() + { + return next($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + if (isset($this->_elements[$key]) || array_key_exists($key, $this->_elements)) { + $removed = $this->_elements[$key]; + unset($this->_elements[$key]); + + return $removed; + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $key = array_search($element, $this->_elements, true); + + if ($key !== false) { + unset($this->_elements[$key]); + + return true; + } + + return false; + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + return $this->add($value); + } + return $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + return $this->remove($offset); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + return isset($this->_elements[$key]) || array_key_exists($key, $this->_elements); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + return in_array($element, $this->_elements, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + return array_search($element, $this->_elements, true); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + if (isset($this->_elements[$key])) { + return $this->_elements[$key]; + } + return null; + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + return array_keys($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + return array_values($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return count($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->_elements[$key] = $value; + } + + /** + * {@inheritDoc} + */ + public function add($value) + { + $this->_elements[] = $value; + return true; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return ! $this->_elements; + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->_elements); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + return new static(array_map($func, $this->_elements)); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + return new static(array_filter($this->_elements, $p)); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + foreach ($this->_elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $coll1 = $coll2 = array(); + foreach ($this->_elements as $key => $element) { + if ($p($key, $element)) { + $coll1[$key] = $element; + } else { + $coll2[$key] = $element; + } + } + return array(new static($coll1), new static($coll2)); + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->_elements = array(); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + return array_slice($this->_elements, $offset, $length, true); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->_elements; + + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + if ($orderings = $criteria->getOrderings()) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == 'DESC' ? -1 : 1, $next); + } + + usort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return new static($filtered); + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 000000000..a0808b38c --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,260 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure, Countable, IteratorAggregate, ArrayAccess; + +/** + * The missing (SPL) Collection/Array/OrderedMap interface. + * + * A Collection resembles the nature of a regular PHP array. That is, + * it is essentially an ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferrable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface Collection extends Countable, IteratorAggregate, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * + * @return boolean Always TRUE. + */ + function add($element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + function clear(); + + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * + * @return boolean TRUE if the collection contains the element, FALSE otherwise. + */ + function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return boolean TRUE if the collection is empty, FALSE otherwise. + */ + function isEmpty(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|integer $key The kex/index of the element to remove. + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + */ + function remove($key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * + * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. + */ + function removeElement($element); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|integer $key The key/index to check for. + * + * @return boolean TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + function containsKey($key); + + /** + * Gets the element at the specified key/index. + * + * @param string|integer $key The key/index of the element to retrieve. + * + * @return mixed + */ + function get($key); + + /** + * Gets all keys/indices of the collection. + * + * @return array The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + */ + function getKeys(); + + /** + * Gets all values of the collection. + * + * @return array The values of all elements in the collection, in the order they + * appear in the collection. + */ + function getValues(); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|integer $key The key/index of the element to set. + * @param mixed $value The element to set. + * + * @return void + */ + function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + */ + function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + */ + function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string + */ + function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + */ + function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + */ + function next(); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + function exists(Closure $p); + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * + * @return Collection A collection with the results of the filter operation. + */ + function filter(Closure $p); + + /** + * Tests whether the given predicate p holds for all elements of this collection. + * + * @param Closure $p The predicate. + * + * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + function forAll(Closure $p); + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @param Closure $func + * + * @return Collection + */ + function map(Closure $func); + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * + * @return array An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + function partition(Closure $p); + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * + * @return int|string|bool The key/index of the element or FALSE if the element was not found. + */ + function indexOf($element); + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return array + */ + function slice($offset, $length = null); +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php new file mode 100644 index 000000000..42929bdca --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,245 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Expression; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Criteria for filtering Selectable collections. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Criteria +{ + /** + * @var string + */ + const ASC = 'ASC'; + + /** + * @var string + */ + const DESC = 'DESC'; + + /** + * @var \Doctrine\Common\Collections\ExpressionBuilder|null + */ + private static $expressionBuilder; + + /** + * @var \Doctrine\Common\Collections\Expr\Expression|null + */ + private $expression; + + /** + * @var array|null + */ + private $orderings; + + /** + * @var int|null + */ + private $firstResult; + + /** + * @var int|null + */ + private $maxResults; + + /** + * Creates an instance of the class. + * + * @return Criteria + */ + public static function create() + { + return new static(); + } + + /** + * Returns the expression builder. + * + * @return \Doctrine\Common\Collections\ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + return self::$expressionBuilder; + } + + /** + * Construct a new Criteria. + * + * @param Expression $expression + * @param array|null $orderings + * @param int|null $firstResult + * @param int|null $maxResults + */ + public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) + { + $this->expression = $expression; + $this->orderings = $orderings; + $this->firstResult = $firstResult; + $this->maxResults = $maxResults; + } + + /** + * Sets the where expression to evaluate when this Criteria is searched for. + * + * @param Expression $expression + * + * @return Criteria + */ + public function where(Expression $expression) + { + $this->expression = $expression; + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an AND with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function andWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an OR with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function orWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array( + $this->expression, $expression + )); + + return $this; + } + + /** + * Gets the expression attached to this Criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Gets the current orderings of this Criteria. + * + * @return array + */ + public function getOrderings() + { + return $this->orderings; + } + + /** + * Sets the ordering of the result of this Criteria. + * + * Keys are field and values are the order, being either ASC or DESC. + * + * @see Criteria::ASC + * @see Criteria::DESC + * + * @param array $orderings + * + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = $orderings; + return $this; + } + + /** + * Gets the current first result option of this Criteria. + * + * @return int|null + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set the number of first result that this Criteria should return. + * + * @param int|null $firstResult The value to set. + * + * @return Criteria + */ + public function setFirstResult($firstResult) + { + $this->firstResult = $firstResult; + return $this; + } + + /** + * Gets maxResults. + * + * @return int|null + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Sets maxResults. + * + * @param int|null $maxResults The value to set. + * + * @return Criteria + */ + public function setMaxResults($maxResults) + { + $this->maxResults = $maxResults; + return $this; + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php new file mode 100644 index 000000000..50994584c --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,227 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Walks an expression graph and turns it into a PHP closure. + * + * This closure can be used with {@Collection#filter()} and is used internally + * by {@ArrayCollection#select()}. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ClosureExpressionVisitor extends ExpressionVisitor +{ + /** + * Accesses the field of a given object. This field has to be public + * directly or indirectly (through an accessor get*, is*, or a magic + * method, __get, __call). + * + * @param object $object + * @param string $field + * + * @return mixed + */ + public static function getObjectFieldValue($object, $field) + { + if (is_array($object)) { + return $object[$field]; + } + + $accessors = array('get', 'is'); + + foreach ($accessors as $accessor) { + $accessor .= $field; + + if ( ! method_exists($object, $accessor)) { + continue; + } + + return $object->$accessor(); + } + + // __call should be triggered for get. + $accessor = $accessors[0] . $field; + + if (method_exists($object, '__call')) { + return $object->$accessor(); + } + + if ($object instanceof \ArrayAccess) { + return $object[$field]; + } + + return $object->$field; + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @param string $name + * @param int $orientation + * @param \Closure $next + * + * @return \Closure + */ + public static function sortByField($name, $orientation = 1, \Closure $next = null) + { + if (!$next) { + $next = function() { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) { + $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); + $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return (($aValue > $bValue) ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + switch ($comparison->getOperator()) { + case Comparison::EQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) { + return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) { + return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + default: + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = array(); + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return $this->andExpressions($expressionList); + + case CompositeExpression::TYPE_OR: + return $this->orExpressions($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * @param array $expressions + * + * @return callable + */ + private function andExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + return true; + }; + } + + /** + * @param array $expressions + * + * @return callable + */ + private function orExpressions($expressions) + { + return function ($object) use ($expressions) { + foreach ($expressions as $expression) { + if ($expression($object)) { + return true; + } + } + return false; + }; + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php new file mode 100644 index 000000000..d54ecf25c --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,103 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Comparison of a field with a value by the given operator. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Comparison implements Expression +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + const IS = '='; // no difference with EQ + const IN = 'IN'; + const NIN = 'NIN'; + const CONTAINS = 'CONTAINS'; + + /** + * @var string + */ + private $field; + + /** + * @var string + */ + private $op; + + /** + * @var Value + */ + private $value; + + /** + * @param string $field + * @param string $operator + * @param mixed $value + */ + public function __construct($field, $operator, $value) + { + if ( ! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->field = $field; + $this->op = $operator; + $this->value = $value; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return Value + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->op; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php new file mode 100644 index 000000000..3613c0274 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression of Expressions combined by AND or OR operation. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class CompositeExpression implements Expression +{ + const TYPE_AND = 'AND'; + const TYPE_OR = 'OR'; + + /** + * @var string + */ + private $type; + + /** + * @var Expression[] + */ + private $expressions = array(); + + /** + * @param string $type + * @param array $expressions + * + * @throws \RuntimeException + */ + public function __construct($type, array $expressions) + { + $this->type = $type; + + foreach ($expressions as $expr) { + if ($expr instanceof Value) { + throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); + } + if ( ! ($expr instanceof Expression)) { + throw new \RuntimeException("No expression given to CompositeExpression."); + } + + $this->expressions[] = $expr; + } + } + + /** + * Returns the list of expressions nested in this composite. + * + * @return Expression[] + */ + public function getExpressionList() + { + return $this->expressions; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php new file mode 100644 index 000000000..68db767c0 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression for the {@link Selectable} interface. + * + * @author Benjamin Eberlei + */ +interface Expression +{ + /** + * @param ExpressionVisitor $visitor + * + * @return mixed + */ + public function visit(ExpressionVisitor $visitor); +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php new file mode 100644 index 000000000..080afdc6d --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * An Expression visitor walks a graph of expressions and turns them into a + * query for the underlying implementation. + * + * @author Benjamin Eberlei + */ +abstract class ExpressionVisitor +{ + /** + * Converts a comparison expression into the target query language output. + * + * @param Comparison $comparison + * + * @return mixed + */ + abstract public function walkComparison(Comparison $comparison); + + /** + * Converts a value expression into the target query language part. + * + * @param Value $value + * + * @return mixed + */ + abstract public function walkValue(Value $value); + + /** + * Converts a composite expression into the target query language output. + * + * @param CompositeExpression $expr + * + * @return mixed + */ + abstract public function walkCompositeExpression(CompositeExpression $expr); + + /** + * Dispatches walking an expression to the appropriate handler. + * + * @param Expression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function dispatch(Expression $expr) + { + switch (true) { + case ($expr instanceof Comparison): + return $this->walkComparison($expr); + + case ($expr instanceof Value): + return $this->walkValue($expr); + + case ($expr instanceof CompositeExpression): + return $this->walkCompositeExpression($expr); + + default: + throw new \RuntimeException("Unknown Expression " . get_class($expr)); + } + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php new file mode 100644 index 000000000..7f6e83143 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +class Value implements Expression +{ + /** + * @var mixed + */ + private $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php new file mode 100644 index 000000000..6539e3c75 --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,166 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Builder for Expressions in the {@link Selectable} interface. + * + * Important Notice for interoperable code: You have to use scalar + * values only for comparisons, otherwise the behavior of the comparision + * may be different between implementations (Array vs ORM vs ODM). + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ExpressionBuilder +{ + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function eq($field, $value) + { + return new Comparison($field, Comparison::EQ, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gt($field, $value) + { + return new Comparison($field, Comparison::GT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lt($field, $value) + { + return new Comparison($field, Comparison::LT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gte($field, $value) + { + return new Comparison($field, Comparison::GTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lte($field, $value) + { + return new Comparison($field, Comparison::LTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function neq($field, $value) + { + return new Comparison($field, Comparison::NEQ, new Value($value)); + } + + /** + * @param string $field + * + * @return Comparison + */ + public function isNull($field) + { + return new Comparison($field, Comparison::EQ, new Value(null)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function in($field, array $values) + { + return new Comparison($field, Comparison::IN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function notIn($field, array $values) + { + return new Comparison($field, Comparison::NIN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function contains($field, $value) + { + return new Comparison($field, Comparison::CONTAINS, new Value($value)); + } +} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php new file mode 100644 index 000000000..401d46e4d --- /dev/null +++ b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Collections; + +/** + * Interface for collections that allow efficient filtering with an expression API. + * + * Goal of this interface is a backend independent method to fetch elements + * from a collections. {@link Expression} is crafted in a way that you can + * implement queries from both in-memory and database-backed collections. + * + * For database backed collections this allows very efficient access by + * utilizing the query APIs, for example SQL in the ORM. Applications using + * this API can implement efficient database access without having to ask the + * EntityManager or Repositories. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +interface Selectable +{ + /** + * Selects all elements from a selectable that match the expression and + * returns a new collection containing these elements. + * + * @param Criteria $criteria + * + * @return Collection + */ + function matching(Criteria $criteria); +} diff --git a/core/lib/doctrine/collections/phpunit.xml.dist b/core/lib/doctrine/collections/phpunit.xml.dist new file mode 100644 index 000000000..36968e99c --- /dev/null +++ b/core/lib/doctrine/collections/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + ./tests/Doctrine/ + + + + + + ./lib/Doctrine/ + + + + + + performance + + + diff --git a/core/lib/guzzle/guzzle/.gitignore b/core/lib/guzzle/guzzle/.gitignore new file mode 100644 index 000000000..893035d5b --- /dev/null +++ b/core/lib/guzzle/guzzle/.gitignore @@ -0,0 +1,27 @@ +# Ingore common cruft +.DS_STORE +coverage +.idea + +# Ignore binary files +guzzle.phar +guzzle-min.phar + +# Ignore potentially sensitive phpunit file +phpunit.xml + +# Ignore composer generated files +composer.phar +composer.lock +composer-test.lock +vendor/ + +# Ignore build files +build/ +phing/build.properties + +# Ignore subsplit working directory +.subsplit + +docs/_build +docs/*.pyc diff --git a/core/lib/guzzle/guzzle/.travis.yml b/core/lib/guzzle/guzzle/.travis.yml new file mode 100644 index 000000000..56d283848 --- /dev/null +++ b/core/lib/guzzle/guzzle/.travis.yml @@ -0,0 +1,30 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl --version + - pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini + - echo 'Installing pecl_http' + - wget --quiet http://pecl.php.net/get/pecl_http-1.7.6.tgz + - tar -xzf pecl_http-1.7.6.tgz + - sh -c "cd pecl_http-1.7.6 && phpize && ./configure && make && sudo make install" > /dev/null + - echo "extension=http.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` + - pecl install uri_template-beta + - phpenv rehash + - composer install --dev + - echo 'Ensuring the correct version of node is running' + - ~/.nvm/nvm.sh install v0.6.14 + +script: vendor/bin/phpunit + +matrix: + allow_failures: + - php: 5.6 + - php: hhvm + fast_finish: true diff --git a/core/lib/guzzle/guzzle/CHANGELOG.md b/core/lib/guzzle/guzzle/CHANGELOG.md new file mode 100644 index 000000000..a1ba31fd3 --- /dev/null +++ b/core/lib/guzzle/guzzle/CHANGELOG.md @@ -0,0 +1,788 @@ +CHANGELOG +========= + +3.9.1 (2014-05-07) +------------------ + +* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. +* Added a fix to the stream checksum function so that when the first read + returns a falsey value, it still continues to consume the stream until EOF. + +3.9.0 (2014-04-23) +------------------ + +* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value + with no trailing "=". See dc1d824277. +* No longer performing an MD5 check on the cacert each time the phar is used, + but rather copying the cacert to the temp directory. +* `"0"` can now be added as a URL path +* Deleting cookies that are set to empty +* If-Modified-Since is no longer unnecessarily added to the CachePlugin +* Cookie path matching now follows RFC 6265 s5.1.4 +* Updated service descriptions are now added to a service client's composite + factory. +* MockPlugin now throws an exception if the queue is empty. +* Properly parsing URLs that start with "http" but are not absolute +* Added the ability to configure the curl_multi_select timeout setting +* OAuth parameters are now sorted using lexicographical byte value ordering +* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin + +3.8.1 (2014-01-28) +------------------ + +* Bug: Always using GET requests when redirecting from a 303 response +* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in + `Guzzle\Http\ClientInterface::setSslVerification()` +* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL +* Bug: The body of a request can now be set to `"0"` +* Sending PHP stream requests no longer forces `HTTP/1.0` +* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of + each sub-exception +* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than + clobbering everything). +* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) +* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. + For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. +* Now properly escaping the regular expression delimiter when matching Cookie domains. +* Network access is now disabled when loading XML documents + +3.8.0 (2013-12-05) +------------------ + +* Added the ability to define a POST name for a file +* JSON response parsing now properly walks additionalProperties +* cURL error code 18 is now retried automatically in the BackoffPlugin +* Fixed a cURL error when URLs contain fragments +* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were + CurlExceptions +* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) +* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` +* Fixed a bug that was encountered when parsing empty header parameters +* UriTemplate now has a `setRegex()` method to match the docs +* The `debug` request parameter now checks if it is truthy rather than if it exists +* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin +* Added the ability to combine URLs using strict RFC 3986 compliance +* Command objects can now return the validation errors encountered by the command +* Various fixes to cache revalidation (#437 and 29797e5) +* Various fixes to the AsyncPlugin +* Cleaned up build scripts + +3.7.4 (2013-10-02) +------------------ + +* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) +* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp + (see https://github.com/aws/aws-sdk-php/issues/147) +* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots +* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) +* Updated the bundled cacert.pem (#419) +* OauthPlugin now supports adding authentication to headers or query string (#425) + +3.7.3 (2013-09-08) +------------------ + +* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and + `CommandTransferException`. +* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description +* Schemas are only injected into response models when explicitly configured. +* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of + an EntityBody. +* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. +* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. +* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() +* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin +* Bug fix: Visiting XML attributes first before visting XML children when serializing requests +* Bug fix: Properly parsing headers that contain commas contained in quotes +* Bug fix: mimetype guessing based on a filename is now case-insensitive + +3.7.2 (2013-08-02) +------------------ + +* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander + See https://github.com/guzzle/guzzle/issues/371 +* Bug fix: Cookie domains are now matched correctly according to RFC 6265 + See https://github.com/guzzle/guzzle/issues/377 +* Bug fix: GET parameters are now used when calculating an OAuth signature +* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted +* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched +* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. + See https://github.com/guzzle/guzzle/issues/379 +* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See + https://github.com/guzzle/guzzle/pull/380 +* cURL multi cleanup and optimizations + +3.7.1 (2013-07-05) +------------------ + +* Bug fix: Setting default options on a client now works +* Bug fix: Setting options on HEAD requests now works. See #352 +* Bug fix: Moving stream factory before send event to before building the stream. See #353 +* Bug fix: Cookies no longer match on IP addresses per RFC 6265 +* Bug fix: Correctly parsing header parameters that are in `<>` and quotes +* Added `cert` and `ssl_key` as request options +* `Host` header can now diverge from the host part of a URL if the header is set manually +* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter +* OAuth parameters are only added via the plugin if they aren't already set +* Exceptions are now thrown when a URL cannot be parsed +* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails +* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin + +3.7.0 (2013-06-10) +------------------ + +* See UPGRADING.md for more information on how to upgrade. +* Requests now support the ability to specify an array of $options when creating a request to more easily modify a + request. You can pass a 'request.options' configuration setting to a client to apply default request options to + every request created by a client (e.g. default query string variables, headers, curl options, etc). +* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. + See `Guzzle\Http\StaticClient::mount`. +* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests + created by a command (e.g. custom headers, query string variables, timeout settings, etc). +* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the + headers of a response +* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key + (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) +* ServiceBuilders now support storing and retrieving arbitrary data +* CachePlugin can now purge all resources for a given URI +* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource +* CachePlugin now uses the Vary header to determine if a resource is a cache hit +* `Guzzle\Http\Message\Response` now implements `\Serializable` +* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters +* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable +* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` +* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size +* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message +* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older + Symfony users can still use the old version of Monolog. +* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. + Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. +* Several performance improvements to `Guzzle\Common\Collection` +* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +* Added `Guzzle\Stream\StreamInterface::isRepeatable` +* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. +* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. +* Removed `Guzzle\Http\ClientInterface::expandTemplate()` +* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` +* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` +* Removed `Guzzle\Http\Message\RequestInterface::canCache` +* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` +* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` +* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. +* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting + `Guzzle\Common\Version::$emitWarnings` to true. +* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use + `$request->getResponseBody()->isRepeatable()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use + `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. + These will work through Guzzle 4.0 +* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. +* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. +* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. +* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +* Marked `Guzzle\Common\Collection::inject()` as deprecated. +* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` +* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +* Always setting X-cache headers on cached responses +* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +* Added `CacheStorageInterface::purge($url)` +* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.6.0 (2013-05-29) +------------------ + +* ServiceDescription now implements ToArrayInterface +* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters +* Guzzle can now correctly parse incomplete URLs +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess +* Added the ability to cast Model objects to a string to view debug information. + +3.5.0 (2013-05-13) +------------------ + +* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times +* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove + itself from the EventDispatcher) +* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values +* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too +* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a + non-existent key +* Bug: All __call() method arguments are now required (helps with mocking frameworks) +* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference + to help with refcount based garbage collection of resources created by sending a request +* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. +* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the + HistoryPlugin for a history. +* Added a `responseBody` alias for the `response_body` location +* Refactored internals to no longer rely on Response::getRequest() +* HistoryPlugin can now be cast to a string +* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests + and responses that are sent over the wire +* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects + +3.4.3 (2013-04-30) +------------------ + +* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response +* Added a check to re-extract the temp cacert bundle from the phar before sending each request + +3.4.2 (2013-04-29) +------------------ + +* Bug fix: Stream objects now work correctly with "a" and "a+" modes +* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present +* Bug fix: AsyncPlugin no longer forces HEAD requests +* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter +* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails +* Setting a response on a request will write to the custom request body from the response body if one is specified +* LogPlugin now writes to php://output when STDERR is undefined +* Added the ability to set multiple POST files for the same key in a single call +* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default +* Added the ability to queue CurlExceptions to the MockPlugin +* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) +* Configuration loading now allows remote files + +3.4.1 (2013-04-16) +------------------ + +* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti + handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. +* Exceptions are now properly grouped when sending requests in parallel +* Redirects are now properly aggregated when a multi transaction fails +* Redirects now set the response on the original object even in the event of a failure +* Bug fix: Model names are now properly set even when using $refs +* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax +* Added support for oauth_callback in OAuth signatures +* Added support for oauth_verifier in OAuth signatures +* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection + +3.4.0 (2013-04-11) +------------------ + +* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 +* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 +* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 +* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. +* Bug fix: Added `number` type to service descriptions. +* Bug fix: empty parameters are removed from an OAuth signature +* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header +* Bug fix: Fixed "array to string" error when validating a union of types in a service description +* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream +* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. +* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. +* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. +* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if + the Content-Type can be determined based on the entity body or the path of the request. +* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. +* Added support for a PSR-3 LogAdapter. +* Added a `command.after_prepare` event +* Added `oauth_callback` parameter to the OauthPlugin +* Added the ability to create a custom stream class when using a stream factory +* Added a CachingEntityBody decorator +* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. +* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. +* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies +* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This + means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use + POST fields or files (the latter is only used when emulating a form POST in the browser). +* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest + +3.3.1 (2013-03-10) +------------------ + +* Added the ability to create PHP streaming responses from HTTP requests +* Bug fix: Running any filters when parsing response headers with service descriptions +* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing +* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across + response location visitors. +* Bug fix: Removed the possibility of creating configuration files with circular dependencies +* RequestFactory::create() now uses the key of a POST file when setting the POST file name +* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set + +3.3.0 (2013-03-03) +------------------ + +* A large number of performance optimizations have been made +* Bug fix: Added 'wb' as a valid write mode for streams +* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned +* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` +* BC: Removed `Guzzle\Http\Utils` class +* BC: Setting a service description on a client will no longer modify the client's command factories. +* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using + the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' +* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to + lowercase +* Operation parameter objects are now lazy loaded internally +* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses +* Added support for instantiating responseType=class responseClass classes. Classes must implement + `Guzzle\Service\Command\ResponseClassInterface` +* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These + additional properties also support locations and can be used to parse JSON responses where the outermost part of the + JSON is an array +* Added support for nested renaming of JSON models (rename sentAs to name) +* CachePlugin + * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error + * Debug headers can now added to cached response in the CachePlugin + +3.2.0 (2013-02-14) +------------------ + +* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. +* URLs with no path no longer contain a "/" by default +* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. +* BadResponseException no longer includes the full request and response message +* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface +* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface +* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription +* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list +* xmlEncoding can now be customized for the XML declaration of a XML service description operation +* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value + aggregation and no longer uses callbacks +* The URL encoding implementation of Guzzle\Http\QueryString can now be customized +* Bug fix: Filters were not always invoked for array service description parameters +* Bug fix: Redirects now use a target response body rather than a temporary response body +* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded +* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives + +3.1.2 (2013-01-27) +------------------ + +* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the + response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. +* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent +* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) +* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() +* Setting default headers on a client after setting the user-agent will not erase the user-agent setting + +3.1.1 (2013-01-20) +------------------ + +* Adding wildcard support to Guzzle\Common\Collection::getPath() +* Adding alias support to ServiceBuilder configs +* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface + +3.1.0 (2013-01-12) +------------------ + +* BC: CurlException now extends from RequestException rather than BadResponseException +* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() +* Added getData to ServiceDescriptionInterface +* Added context array to RequestInterface::setState() +* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http +* Bug: Adding required content-type when JSON request visitor adds JSON to a command +* Bug: Fixing the serialization of a service description with custom data +* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing + an array of successful and failed responses +* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection +* Added Guzzle\Http\IoEmittingEntityBody +* Moved command filtration from validators to location visitors +* Added `extends` attributes to service description parameters +* Added getModels to ServiceDescriptionInterface + +3.0.7 (2012-12-19) +------------------ + +* Fixing phar detection when forcing a cacert to system if null or true +* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` +* Cleaning up `Guzzle\Common\Collection::inject` method +* Adding a response_body location to service descriptions + +3.0.6 (2012-12-09) +------------------ + +* CurlMulti performance improvements +* Adding setErrorResponses() to Operation +* composer.json tweaks + +3.0.5 (2012-11-18) +------------------ + +* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin +* Bug: Response body can now be a string containing "0" +* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert +* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs +* Added support for XML attributes in service description responses +* DefaultRequestSerializer now supports array URI parameter values for URI template expansion +* Added better mimetype guessing to requests and post files + +3.0.4 (2012-11-11) +------------------ + +* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value +* Bug: Cookies can now be added that have a name, domain, or value set to "0" +* Bug: Using the system cacert bundle when using the Phar +* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures +* Enhanced cookie jar de-duplication +* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added +* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies +* Added the ability to create any sort of hash for a stream rather than just an MD5 hash + +3.0.3 (2012-11-04) +------------------ + +* Implementing redirects in PHP rather than cURL +* Added PECL URI template extension and using as default parser if available +* Bug: Fixed Content-Length parsing of Response factory +* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. +* Adding ToArrayInterface throughout library +* Fixing OauthPlugin to create unique nonce values per request + +3.0.2 (2012-10-25) +------------------ + +* Magic methods are enabled by default on clients +* Magic methods return the result of a command +* Service clients no longer require a base_url option in the factory +* Bug: Fixed an issue with URI templates where null template variables were being expanded + +3.0.1 (2012-10-22) +------------------ + +* Models can now be used like regular collection objects by calling filter, map, etc +* Models no longer require a Parameter structure or initial data in the constructor +* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` + +3.0.0 (2012-10-15) +------------------ + +* Rewrote service description format to be based on Swagger + * Now based on JSON schema + * Added nested input structures and nested response models + * Support for JSON and XML input and output models + * Renamed `commands` to `operations` + * Removed dot class notation + * Removed custom types +* Broke the project into smaller top-level namespaces to be more component friendly +* Removed support for XML configs and descriptions. Use arrays or JSON files. +* Removed the Validation component and Inspector +* Moved all cookie code to Guzzle\Plugin\Cookie +* Magic methods on a Guzzle\Service\Client now return the command un-executed. +* Calling getResult() or getResponse() on a command will lazily execute the command if needed. +* Now shipping with cURL's CA certs and using it by default +* Added previousResponse() method to response objects +* No longer sending Accept and Accept-Encoding headers on every request +* Only sending an Expect header by default when a payload is greater than 1MB +* Added/moved client options: + * curl.blacklist to curl.option.blacklist + * Added ssl.certificate_authority +* Added a Guzzle\Iterator component +* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin +* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) +* Added a more robust caching plugin +* Added setBody to response objects +* Updating LogPlugin to use a more flexible MessageFormatter +* Added a completely revamped build process +* Cleaning up Collection class and removing default values from the get method +* Fixed ZF2 cache adapters + +2.8.8 (2012-10-15) +------------------ + +* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did + +2.8.7 (2012-09-30) +------------------ + +* Bug: Fixed config file aliases for JSON includes +* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests +* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload +* Bug: Hardening request and response parsing to account for missing parts +* Bug: Fixed PEAR packaging +* Bug: Fixed Request::getInfo +* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail +* Adding the ability for the namespace Iterator factory to look in multiple directories +* Added more getters/setters/removers from service descriptions +* Added the ability to remove POST fields from OAuth signatures +* OAuth plugin now supports 2-legged OAuth + +2.8.6 (2012-09-05) +------------------ + +* Added the ability to modify and build service descriptions +* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command +* Added a `json` parameter location +* Now allowing dot notation for classes in the CacheAdapterFactory +* Using the union of two arrays rather than an array_merge when extending service builder services and service params +* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references + in service builder config files. +* Services defined in two different config files that include one another will by default replace the previously + defined service, but you can now create services that extend themselves and merge their settings over the previous +* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like + '_default' with a default JSON configuration file. + +2.8.5 (2012-08-29) +------------------ + +* Bug: Suppressed empty arrays from URI templates +* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching +* Added support for HTTP responses that do not contain a reason phrase in the start-line +* AbstractCommand commands are now invokable +* Added a way to get the data used when signing an Oauth request before a request is sent + +2.8.4 (2012-08-15) +------------------ + +* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin +* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. +* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream +* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream +* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) +* Added additional response status codes +* Removed SSL information from the default User-Agent header +* DELETE requests can now send an entity body +* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries +* Added the ability of the MockPlugin to consume mocked request bodies +* LogPlugin now exposes request and response objects in the extras array + +2.8.3 (2012-07-30) +------------------ + +* Bug: Fixed a case where empty POST requests were sent as GET requests +* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body +* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new +* Added multiple inheritance to service description commands +* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` +* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything +* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles + +2.8.2 (2012-07-24) +------------------ + +* Bug: Query string values set to 0 are no longer dropped from the query string +* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` +* Bug: ``+`` is now treated as an encoded space when parsing query strings +* QueryString and Collection performance improvements +* Allowing dot notation for class paths in filters attribute of a service descriptions + +2.8.1 (2012-07-16) +------------------ + +* Loosening Event Dispatcher dependency +* POST redirects can now be customized using CURLOPT_POSTREDIR + +2.8.0 (2012-07-15) +------------------ + +* BC: Guzzle\Http\Query + * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) + * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() + * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) + * Changed the aggregation functions of QueryString to be static methods + * Can now use fromString() with querystrings that have a leading ? +* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters +* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body +* Cookies are no longer URL decoded by default +* Bug: URI template variables set to null are no longer expanded + +2.7.2 (2012-07-02) +------------------ + +* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. +* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() +* CachePlugin now allows for a custom request parameter function to check if a request can be cached +* Bug fix: CachePlugin now only caches GET and HEAD requests by default +* Bug fix: Using header glue when transferring headers over the wire +* Allowing deeply nested arrays for composite variables in URI templates +* Batch divisors can now return iterators or arrays + +2.7.1 (2012-06-26) +------------------ + +* Minor patch to update version number in UA string +* Updating build process + +2.7.0 (2012-06-25) +------------------ + +* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. +* BC: Removed magic setX methods from commands +* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method +* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. +* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) +* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace +* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin +* Added the ability to set POST fields and files in a service description +* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method +* Adding a command.before_prepare event to clients +* Added BatchClosureTransfer and BatchClosureDivisor +* BatchTransferException now includes references to the batch divisor and transfer strategies +* Fixed some tests so that they pass more reliably +* Added Guzzle\Common\Log\ArrayLogAdapter + +2.6.6 (2012-06-10) +------------------ + +* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin +* BC: Removing Guzzle\Service\Command\CommandSet +* Adding generic batching system (replaces the batch queue plugin and command set) +* Updating ZF cache and log adapters and now using ZF's composer repository +* Bug: Setting the name of each ApiParam when creating through an ApiCommand +* Adding result_type, result_doc, deprecated, and doc_url to service descriptions +* Bug: Changed the default cookie header casing back to 'Cookie' + +2.6.5 (2012-06-03) +------------------ + +* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() +* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from +* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data +* BC: Renaming methods in the CookieJarInterface +* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations +* Making the default glue for HTTP headers ';' instead of ',' +* Adding a removeValue to Guzzle\Http\Message\Header +* Adding getCookies() to request interface. +* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +2.6.4 (2012-05-30) +------------------ + +* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. +* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand +* Bug: Fixing magic method command calls on clients +* Bug: Email constraint only validates strings +* Bug: Aggregate POST fields when POST files are present in curl handle +* Bug: Fixing default User-Agent header +* Bug: Only appending or prepending parameters in commands if they are specified +* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes +* Allowing the use of dot notation for class namespaces when using instance_of constraint +* Added any_match validation constraint +* Added an AsyncPlugin +* Passing request object to the calculateWait method of the ExponentialBackoffPlugin +* Allowing the result of a command object to be changed +* Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +2.6.3 (2012-05-23) +------------------ + +* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. +* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. +* You can now use an array of data when creating PUT request bodies in the request factory. +* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. +* [Http] Adding support for Content-Type in multipart POST uploads per upload +* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) +* Adding more POST data operations for easier manipulation of POST data. +* You can now set empty POST fields. +* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. +* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. +* CS updates + +2.6.2 (2012-05-19) +------------------ + +* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +2.6.1 (2012-05-19) +------------------ + +* [BC] Removing 'path' support in service descriptions. Use 'uri'. +* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. +* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. +* [BC] Removing Guzzle\Common\XmlElement. +* All commands, both dynamic and concrete, have ApiCommand objects. +* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. +* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. +* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +2.6.0 (2012-05-15) +------------------ + +* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder +* [BC] Executing a Command returns the result of the command rather than the command +* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. +* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. +* [BC] Moving ResourceIterator* to Guzzle\Service\Resource +* [BC] Completely refactored ResourceIterators to iterate over a cloned command object +* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate +* [BC] Guzzle\Guzzle is now deprecated +* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject +* Adding Guzzle\Version class to give version information about Guzzle +* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() +* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data +* ServiceDescription and ServiceBuilder are now cacheable using similar configs +* Changing the format of XML and JSON service builder configs. Backwards compatible. +* Cleaned up Cookie parsing +* Trimming the default Guzzle User-Agent header +* Adding a setOnComplete() method to Commands that is called when a command completes +* Keeping track of requests that were mocked in the MockPlugin +* Fixed a caching bug in the CacheAdapterFactory +* Inspector objects can be injected into a Command object +* Refactoring a lot of code and tests to be case insensitive when dealing with headers +* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL +* Adding the ability to set global option overrides to service builder configs +* Adding the ability to include other service builder config files from within XML and JSON files +* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +2.5.0 (2012-05-08) +------------------ + +* Major performance improvements +* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. +* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. +* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" +* Added the ability to passed parameters to all requests created by a client +* Added callback functionality to the ExponentialBackoffPlugin +* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. +* Rewinding request stream bodies when retrying requests +* Exception is thrown when JSON response body cannot be decoded +* Added configurable magic method calls to clients and commands. This is off by default. +* Fixed a defect that added a hash to every parsed URL part +* Fixed duplicate none generation for OauthPlugin. +* Emitting an event each time a client is generated by a ServiceBuilder +* Using an ApiParams object instead of a Collection for parameters of an ApiCommand +* cache.* request parameters should be renamed to params.cache.* +* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. +* Added the ability to disable type validation of service descriptions +* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/core/lib/guzzle/guzzle/LICENSE b/core/lib/guzzle/guzzle/LICENSE new file mode 100644 index 000000000..d51aa6986 --- /dev/null +++ b/core/lib/guzzle/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/guzzle/guzzle/README.md b/core/lib/guzzle/guzzle/README.md new file mode 100644 index 000000000..e1fc0cc5a --- /dev/null +++ b/core/lib/guzzle/guzzle/README.md @@ -0,0 +1,36 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) + [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3) + +Guzzle is a PHP HTTP client and framework for building RESTful web service clients. + +- Extremely powerful API provides all the power of cURL with a simple interface. +- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. +- Service description DSL allows you build awesome web service clients faster. +- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. + +Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) + +### Installing via Composer + +The recommended way to install Guzzle is through [Composer](http://getcomposer.org). + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/guzzle:~3.9 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` + +# This is an older version + +This repository is for Guzzle 3.x. Guzzle 4.0, the new version of Guzzle has been released and is available at https://github.com/guzzle/guzzle. diff --git a/core/lib/guzzle/guzzle/UPGRADING.md b/core/lib/guzzle/guzzle/UPGRADING.md new file mode 100644 index 000000000..f58bf1171 --- /dev/null +++ b/core/lib/guzzle/guzzle/UPGRADING.md @@ -0,0 +1,537 @@ +Guzzle Upgrade Guide +==================== + +3.6 to 3.7 +---------- + +### Deprecations + +- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: + +```php +\Guzzle\Common\Version::$emitWarnings = true; +``` + +The following APIs and options have been marked as deprecated: + +- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. +- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. +- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated +- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. +- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. +- Marked `Guzzle\Common\Collection::inject()` as deprecated. +- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use + `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or + `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` + +3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational +request methods. When paired with a client's configuration settings, these options allow you to specify default settings +for various aspects of a request. Because these options make other previous configuration options redundant, several +configuration options and methods of a client and AbstractCommand have been deprecated. + +- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. +- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. +- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` +- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 + + $command = $client->getCommand('foo', array( + 'command.headers' => array('Test' => '123'), + 'command.response_body' => '/path/to/file' + )); + + // Should be changed to: + + $command = $client->getCommand('foo', array( + 'command.request_options' => array( + 'headers' => array('Test' => '123'), + 'save_as' => '/path/to/file' + ) + )); + +### Interface changes + +Additions and changes (you will need to update any implementations or subclasses you may have created): + +- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: + createRequest, head, delete, put, patch, post, options, prepareRequest +- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` +- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` +- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to + `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a + resource, string, or EntityBody into the $options parameter to specify the download location of the response. +- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a + default `array()` +- Added `Guzzle\Stream\StreamInterface::isRepeatable` +- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. + +The following methods were removed from interfaces. All of these methods are still available in the concrete classes +that implement them, but you should update your code to use alternative methods: + +- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use + `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or + `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or + `$client->setDefaultOption('headers/{header_name}', 'value')`. or + `$client->setDefaultOption('headers', array('header_name' => 'value'))`. +- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. +- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. +- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. +- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. +- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. +- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. + +### Cache plugin breaking changes + +- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a + CacheStorageInterface. These two objects and interface will be removed in a future version. +- Always setting X-cache headers on cached responses +- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin +- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface + $request, Response $response);` +- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` +- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` +- Added `CacheStorageInterface::purge($url)` +- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin + $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, + CanCacheStrategyInterface $canCache = null)` +- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` + +3.5 to 3.6 +---------- + +* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. +* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution +* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). + For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). + Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. +* Specific header implementations can be created for complex headers. When a message creates a header, it uses a + HeaderFactory which can map specific headers to specific header classes. There is now a Link header and + CacheControl header implementation. +* Moved getLinks() from Response to just be used on a Link header object. + +If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the +HeaderInterface (e.g. toArray(), getAll(), etc). + +### Interface changes + +* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate +* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() +* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in + Guzzle\Http\Curl\RequestMediator +* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. +* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface +* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() + +### Removed deprecated functions + +* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() +* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). + +### Deprecations + +* The ability to case-insensitively search for header values +* Guzzle\Http\Message\Header::hasExactHeader +* Guzzle\Http\Message\Header::raw. Use getAll() +* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object + instead. + +### Other changes + +* All response header helper functions return a string rather than mixing Header objects and strings inconsistently +* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle + directly via interfaces +* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist + but are a no-op until removed. +* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a + `Guzzle\Service\Command\ArrayCommandInterface`. +* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response + on a request while the request is still being transferred +* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess + +3.3 to 3.4 +---------- + +Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. + +3.2 to 3.3 +---------- + +### Response::getEtag() quote stripping removed + +`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header + +### Removed `Guzzle\Http\Utils` + +The `Guzzle\Http\Utils` class was removed. This class was only used for testing. + +### Stream wrapper and type + +`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. + +### curl.emit_io became emit_io + +Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the +'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' + +3.1 to 3.2 +---------- + +### CurlMulti is no longer reused globally + +Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added +to a single client can pollute requests dispatched from other clients. + +If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the +ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is +created. + +```php +$multi = new Guzzle\Http\Curl\CurlMulti(); +$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); +$builder->addListener('service_builder.create_client', function ($event) use ($multi) { + $event['client']->setCurlMulti($multi); +} +}); +``` + +### No default path + +URLs no longer have a default path value of '/' if no path was specified. + +Before: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com/ +``` + +After: + +```php +$request = $client->get('http://www.foo.com'); +echo $request->getUrl(); +// >> http://www.foo.com +``` + +### Less verbose BadResponseException + +The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and +response information. You can, however, get access to the request and response object by calling `getRequest()` or +`getResponse()` on the exception object. + +### Query parameter aggregation + +Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a +setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is +responsible for handling the aggregation of multi-valued query string variables into a flattened hash. + +2.8 to 3.x +---------- + +### Guzzle\Service\Inspector + +Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` + +**Before** + +```php +use Guzzle\Service\Inspector; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Inspector::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +**After** + +```php +use Guzzle\Common\Collection; + +class YourClient extends \Guzzle\Service\Client +{ + public static function factory($config = array()) + { + $default = array(); + $required = array('base_url', 'username', 'api_key'); + $config = Collection::fromConfig($config, $default, $required); + + $client = new self( + $config->get('base_url'), + $config->get('username'), + $config->get('api_key') + ); + $client->setConfig($config); + + $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); + + return $client; + } +``` + +### Convert XML Service Descriptions to JSON + +**Before** + +```xml + + + + + + Get a list of groups + + + Uses a search query to get a list of groups + + + + Create a group + + + + + Delete a group by ID + + + + + + + Update a group + + + + + + +``` + +**After** + +```json +{ + "name": "Zendesk REST API v2", + "apiVersion": "2012-12-31", + "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", + "operations": { + "list_groups": { + "httpMethod":"GET", + "uri": "groups.json", + "summary": "Get a list of groups" + }, + "search_groups":{ + "httpMethod":"GET", + "uri": "search.json?query=\"{query} type:group\"", + "summary": "Uses a search query to get a list of groups", + "parameters":{ + "query":{ + "location": "uri", + "description":"Zendesk Search Query", + "type": "string", + "required": true + } + } + }, + "create_group": { + "httpMethod":"POST", + "uri": "groups.json", + "summary": "Create a group", + "parameters":{ + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + }, + "delete_group": { + "httpMethod":"DELETE", + "uri": "groups/{id}.json", + "summary": "Delete a group", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to delete by ID", + "type": "integer", + "required": true + } + } + }, + "get_group": { + "httpMethod":"GET", + "uri": "groups/{id}.json", + "summary": "Get a ticket", + "parameters":{ + "id":{ + "location": "uri", + "description":"Group to get by ID", + "type": "integer", + "required": true + } + } + }, + "update_group": { + "httpMethod":"PUT", + "uri": "groups/{id}.json", + "summary": "Update a group", + "parameters":{ + "id": { + "location": "uri", + "description":"Group to update by ID", + "type": "integer", + "required": true + }, + "data": { + "type": "array", + "location": "body", + "description":"Group JSON", + "filters": "json_encode", + "required": true + }, + "Content-Type":{ + "type": "string", + "location":"header", + "static": "application/json" + } + } + } +} +``` + +### Guzzle\Service\Description\ServiceDescription + +Commands are now called Operations + +**Before** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getCommands(); // @returns ApiCommandInterface[] +$sd->hasCommand($name); +$sd->getCommand($name); // @returns ApiCommandInterface|null +$sd->addCommand($command); // @param ApiCommandInterface $command +``` + +**After** + +```php +use Guzzle\Service\Description\ServiceDescription; + +$sd = new ServiceDescription(); +$sd->getOperations(); // @returns OperationInterface[] +$sd->hasOperation($name); +$sd->getOperation($name); // @returns OperationInterface|null +$sd->addOperation($operation); // @param OperationInterface $operation +``` + +### Guzzle\Common\Inflection\Inflector + +Namespace is now `Guzzle\Inflection\Inflector` + +### Guzzle\Http\Plugin + +Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. + +### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log + +Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. + +**Before** + +```php +use Guzzle\Common\Log\ClosureLogAdapter; +use Guzzle\Http\Plugin\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $verbosity is an integer indicating desired message verbosity level +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); +``` + +**After** + +```php +use Guzzle\Log\ClosureLogAdapter; +use Guzzle\Log\MessageFormatter; +use Guzzle\Plugin\Log\LogPlugin; + +/** @var \Guzzle\Http\Client */ +$client; + +// $format is a string indicating desired message format -- @see MessageFormatter +$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); +``` + +### Guzzle\Http\Plugin\CurlAuthPlugin + +Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. + +### Guzzle\Http\Plugin\ExponentialBackoffPlugin + +Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. + +**Before** + +```php +use Guzzle\Http\Plugin\ExponentialBackoffPlugin; + +$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( + ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) + )); + +$client->addSubscriber($backoffPlugin); +``` + +**After** + +```php +use Guzzle\Plugin\Backoff\BackoffPlugin; +use Guzzle\Plugin\Backoff\HttpBackoffStrategy; + +// Use convenient factory method instead -- see implementation for ideas of what +// you can do with chaining backoff strategies +$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( + HttpBackoffStrategy::getDefaultFailureCodes(), array(429) + )); +$client->addSubscriber($backoffPlugin); +``` + +### Known Issues + +#### [BUG] Accept-Encoding header behavior changed unintentionally. + +(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) + +In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to +properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. +See issue #217 for a workaround, or use a version containing the fix. diff --git a/core/lib/guzzle/guzzle/build.xml b/core/lib/guzzle/guzzle/build.xml new file mode 100644 index 000000000..2aa62ba9a --- /dev/null +++ b/core/lib/guzzle/guzzle/build.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/guzzle/guzzle/composer.json b/core/lib/guzzle/guzzle/composer.json new file mode 100644 index 000000000..d6e9a9693 --- /dev/null +++ b/core/lib/guzzle/guzzle/composer.json @@ -0,0 +1,74 @@ +{ + "name": "guzzle/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + + "require": { + "php": ">=5.3.3", + "ext-curl": "*", + "symfony/event-dispatcher": "~2.1" + }, + + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + + "require-dev": { + "doctrine/cache": "~1.3", + "symfony/class-loader": "~2.1", + "monolog/monolog": "~1.0", + "psr/log": "~1.0", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3", + "phpunit/phpunit": "3.7.*" + }, + + "extra": { + "branch-alias": { + "dev-master": "3.8-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/phar-stub.php b/core/lib/guzzle/guzzle/phar-stub.php new file mode 100644 index 000000000..504dfe078 --- /dev/null +++ b/core/lib/guzzle/guzzle/phar-stub.php @@ -0,0 +1,28 @@ +registerNamespaces(array( + 'Guzzle' => 'phar://guzzle.phar/src', + 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', + 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', + 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' +)); +$classLoader->register(); + +// Copy the cacert.pem file from the phar if it is not in the temp folder. +$from = 'phar://guzzle.phar/src/Guzzle/Http/Resources/cacert.pem'; +$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; + +// Only copy when the file size is different +if (!file_exists($certFile) || filesize($certFile) != filesize($from)) { + if (!copy($from, $certFile)) { + throw new RuntimeException("Could not copy {$from} to {$certFile}: " + . var_export(error_get_last(), true)); + } +} + +__HALT_COMPILER(); diff --git a/core/lib/guzzle/guzzle/phing/build.properties.dist b/core/lib/guzzle/guzzle/phing/build.properties.dist new file mode 100644 index 000000000..c60d3d9cf --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/build.properties.dist @@ -0,0 +1,16 @@ +# you may need to update this if you're working on a fork. +guzzle.remote=git@github.com:guzzle/guzzle.git + +# github credentials -- only used by GitHub API calls to create subtree repos +github.basicauth=username:password +# for the subtree split and testing +github.org=guzzle + +# your git path +cmd.git=git + +# your composer command +cmd.composer=composer + +# test server start +cmd.testserver=node diff --git a/core/lib/guzzle/guzzle/phing/imports/dependencies.xml b/core/lib/guzzle/guzzle/phing/imports/dependencies.xml new file mode 100644 index 000000000..e40e037c2 --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/imports/dependencies.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + using git at ${cmd.git} + + + + found git at ${cmd.git} + + + + + + + + + + diff --git a/core/lib/guzzle/guzzle/phing/imports/deploy.xml b/core/lib/guzzle/guzzle/phing/imports/deploy.xml new file mode 100644 index 000000000..109e5ec4f --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/imports/deploy.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + On branch ${head} + + + + + + + + + + working directory clean + + + ${git.status} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ChangeLog Match: ${version.changelog} + Guzzle\Common\Version Match: ${version.version} + + + + releasing: phing -Dnew.version=3.0.x -Dhead=master release + -- + + + + + + + + + + + + + + + BEGINNING RELEASE FOR ${new.version} + + + + + + + + + + + + + + + + + + + + + + + + Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php b/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php new file mode 100644 index 000000000..3b7040982 --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php @@ -0,0 +1,152 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; + +class ComposerLintTask extends Task +{ + protected $dir = null; + protected $file = null; + protected $passthru = false; + protected $composer = null; + + /** + * The setter for the dir + * + * @param string $str Directory to crawl recursively for composer files + */ + public function setDir($str) + { + $this->dir = $str; + } + + /** + * The setter for the file + * + * @param string $str Individual file to validate + */ + public function setFile($str) + { + $this->file = $str; + } + + /** + * Whether to use PHP's passthru() function instead of exec() + * + * @param boolean $passthru If passthru shall be used + */ + public function setPassthru($passthru) + { + $this->passthru = (bool) $passthru; + } + + /** + * Composer to execute. If unset, will attempt composer.phar in project + * basedir, and if that fails, will attempt global composer + * installation. + * + * @param string $str Individual file to validate + */ + public function setComposer($str) + { + $this->file = $str; + } + + /** + * The init method: do init steps + */ + public function init() + { + // nothing needed here + } + + /** + * The main entry point + */ + public function main() + { + if ($this->composer === null) { + $this->findComposer(); + } + + $files = array(); + if (!empty($this->file) && file_exists($this->file)) { + $files[] = $this->file; + } + + if (!empty($this->dir)) { + $found = $this->findFiles(); + foreach ($found as $file) { + $files[] = $this->dir . DIRECTORY_SEPARATOR . $file; + } + } + + foreach ($files as $file) { + + $cmd = $this->composer . ' validate ' . $file; + $cmd = escapeshellcmd($cmd); + + if ($this->passthru) { + $retval = null; + passthru($cmd, $retval); + if ($retval == 1) { + throw new BuildException('invalid composer.json'); + } + } else { + $out = array(); + $retval = null; + exec($cmd, $out, $retval); + if ($retval == 1) { + $err = join("\n", $out); + throw new BuildException($err); + } else { + $this->log($out[0]); + } + } + + } + + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findFiles() + { + $ds = new DirectoryScanner(); + $ds->setBasedir($this->dir); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + return $ds->getIncludedFiles(); + } + + /** + * Find composer installation + * + */ + protected function findComposer() + { + $basedir = $this->project->getBasedir(); + $php = $this->project->getProperty('php.interpreter'); + + if (file_exists($basedir . '/composer.phar')) { + $this->composer = "$php $basedir/composer.phar"; + } else { + $out = array(); + exec('which composer', $out); + if (empty($out)) { + throw new BuildException( + 'Could not determine composer location.' + ); + } + $this->composer = $out[0]; + } + } +} diff --git a/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php b/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php new file mode 100644 index 000000000..f72a6b5d0 --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php @@ -0,0 +1,338 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/Task.php'; +require_once 'PEAR/PackageFileManager2.php'; +require_once 'PEAR/PackageFileManager/File.php'; +require_once 'PEAR/Packager.php'; + +class GuzzlePearPharPackageTask extends Task +{ + private $version; + private $deploy = true; + private $makephar = true; + + private $subpackages = array(); + + public function setVersion($str) + { + $this->version = $str; + } + + public function getVersion() + { + return $this->version; + } + + public function setDeploy($deploy) + { + $this->deploy = (bool) $deploy; + } + + public function getDeploy() + { + return $this->deploy; + } + + public function setMakephar($makephar) + { + $this->makephar = (bool) $makephar; + } + + public function getMakephar() + { + return $this->makephar; + } + + private $basedir; + private $guzzleinfo; + private $changelog_release_date; + private $changelog_notes = '-'; + + public function main() + { + $this->basedir = $this->getProject()->getBasedir(); + + if (!is_dir((string) $this->basedir.'/.subsplit')) { + throw new BuildException('PEAR packaging requires .subsplit directory'); + } + + // main composer file + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json'); + $this->guzzleinfo = json_decode($composer_file, true); + + // make sure we have a target + $pearwork = (string) $this->basedir . '/build/pearwork'; + if (!is_dir($pearwork)) { + mkdir($pearwork, 0777, true); + } + $pearlogs = (string) $this->basedir . '/build/artifacts/logs'; + if (!is_dir($pearlogs)) { + mkdir($pearlogs, 0777, true); + } + + $version = $this->getVersion(); + $this->grabChangelog(); + if ($version[0] == '2') { + $this->log('building single PEAR package'); + $this->buildSinglePackage(); + } else { + // $this->log("building PEAR subpackages"); + // $this->createSubPackages(); + // $this->log("building PEAR bundle package"); + $this->buildSinglePackage(); + } + + if ($this->getMakephar()) { + $this->log("building PHAR"); + $this->getProject()->executeTarget('package-phar'); + } + + if ($this->getDeploy()) { + $this->doDeployment(); + } + } + + public function doDeployment() + { + $basedir = (string) $this->basedir; + $this->log('beginning PEAR/PHAR deployment'); + + chdir($basedir . '/build/pearwork'); + if (!is_dir('./channel')) { + mkdir('./channel'); + } + + // Pull the PEAR channel down locally + passthru('aws s3 sync s3://pear.guzzlephp.org ./channel'); + + // add PEAR packages + foreach (scandir('./') as $file) { + if (substr($file, -4) == '.tgz') { + passthru('pirum add ./channel ' . $file); + } + } + + // if we have a new phar, add it + if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) { + rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar'); + } + + // Sync up with the S3 bucket + chdir($basedir . '/build/pearwork/channel'); + passthru('aws s3 sync . s3://pear.guzzlephp.org'); + } + + public function buildSinglePackage() + { + $v = $this->getVersion(); + $apiversion = $v[0] . '.0.0'; + + $opts = array( + 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/', + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json'), + 'baseinstalldir' => '/', + 'packagefile' => 'package.xml' + //'outputdirectory' => (string) $this->basedir . '/build/pearwork/' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->addRole('md', 'doc'); + $pfm->addRole('pem', 'php'); + $pfm->setPackage('Guzzle'); + $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+"); + $pfm->setDescription($this->guzzleinfo['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion($apiversion); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->addExtensionDep('required', 'curl'); + $pfm->setPearinstallerDep('1.4.6'); + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + if (!empty($this->subpackages)) { + foreach ($this->subpackages as $package) { + $pkg = dirname($package); + $pkg = str_replace('/', '_', $pkg); + $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion); + } + } + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log); + chdir($startdir); + } + + public function createSubPackages() + { + $this->findComponents(); + + foreach ($this->subpackages as $package) { + $baseinstalldir = dirname($package); + $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir; + $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package); + $package_info = json_decode($composer_file, true); + $this->log('building ' . $package_info['target-dir'] . ' subpackage'); + $this->buildSubPackage($dir, $baseinstalldir, $package_info); + } + } + + public function buildSubPackage($dir, $baseinstalldir, $info) + { + $package = str_replace('/', '_', $baseinstalldir); + $opts = array( + 'packagedirectory' => $dir, + 'filelistgenerator' => 'file', + 'ignore' => array('*composer.json', '*package.xml'), + 'baseinstalldir' => '/' . $info['target-dir'], + 'packagefile' => 'package.xml' + ); + $pfm = new PEAR_PackageFileManager2(); + $pfm->setOptions($opts); + $pfm->setPackage($package); + $pfm->setSummary($info['description']); + $pfm->setDescription($info['description']); + $pfm->setPackageType('php'); + $pfm->setChannel('guzzlephp.org/pear'); + $pfm->setAPIVersion('3.0.0'); + $pfm->setReleaseVersion($this->getVersion()); + $pfm->setAPIStability('stable'); + $pfm->setReleaseStability('stable'); + $pfm->setNotes($this->changelog_notes); + $pfm->setPackageType('php'); + $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); + $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); + $pfm->setDate($this->changelog_release_date); + $pfm->generateContents(); + + $phpdep = $this->guzzleinfo['require']['php']; + $phpdep = str_replace('>=', '', $phpdep); + $pfm->setPhpDep($phpdep); + $pfm->setPearinstallerDep('1.4.6'); + + foreach ($info['require'] as $type => $version) { + if ($type == 'php') { + continue; + } + if ($type == 'symfony/event-dispatcher') { + $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); + } + if ($type == 'ext-curl') { + $pfm->addExtensionDep('required', 'curl'); + } + if (substr($type, 0, 6) == 'guzzle') { + $gdep = str_replace('/', ' ', $type); + $gdep = ucwords($gdep); + $gdep = str_replace(' ', '_', $gdep); + $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion()); + } + } + + // can't have main Guzzle package AND sub-packages + $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion); + + ob_start(); + $startdir = getcwd(); + chdir((string) $this->basedir . '/build/pearwork'); + + echo "DEBUGGING GENERATED PACKAGE FILE\n"; + $result = $pfm->debugPackageFile(); + if ($result) { + $out = $pfm->writePackageFile(); + echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; + var_dump($out); + // load up package file and build package + $packager = new PEAR_Packager(); + echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; + $dest_package = $packager->package($opts['packagedirectory'].'/package.xml'); + var_dump($dest_package); + } else { + echo "\n\n\nDEBUGGING RESULT:\n"; + var_dump($result); + } + echo "removing package.xml"; + unlink($opts['packagedirectory'].'/package.xml'); + $log = ob_get_clean(); + file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log); + chdir($startdir); + } + + public function findComponents() + { + $ds = new DirectoryScanner(); + $ds->setBasedir((string) $this->basedir.'/.subsplit/src'); + $ds->setIncludes(array('**/composer.json')); + $ds->scan(); + $files = $ds->getIncludedFiles(); + $this->subpackages = $files; + } + + public function grabChangelog() + { + $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md'); + $notes = ''; + $in_version = false; + $release_date = null; + + foreach ($cl as $line) { + $line = trim($line); + if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) { + $release_date = $matches[1]; + $in_version = true; + continue; + } + if ($in_version && empty($line) && empty($notes)) { + continue; + } + if ($in_version && ! empty($line)) { + $notes .= $line."\n"; + } + if ($in_version && empty($line) && !empty($notes)) { + $in_version = false; + } + } + $this->changelog_release_date = $release_date; + + if (! empty($notes)) { + $this->changelog_notes = $notes; + } + } +} diff --git a/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php b/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php new file mode 100644 index 000000000..5d56a5bd5 --- /dev/null +++ b/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php @@ -0,0 +1,385 @@ + + * @license http://claylo.mit-license.org/2012/ MIT License + */ + +require_once 'phing/tasks/ext/git/GitBaseTask.php'; + +// base - base of tree to split out +// subIndicatorFile - composer.json, package.xml? +class GuzzleSubSplitTask extends GitBaseTask +{ + /** + * What git repository to pull from and publish to + */ + protected $remote = null; + + /** + * Publish for comma-separated heads instead of all heads + */ + protected $heads = null; + + /** + * Publish for comma-separated tags instead of all tags + */ + protected $tags = null; + + /** + * Base of the tree RELATIVE TO .subsplit working dir + */ + protected $base = null; + + /** + * The presence of this file will indicate that the directory it resides + * in is at the top level of a split. + */ + protected $subIndicatorFile = 'composer.json'; + + /** + * Do everything except actually send the update. + */ + protected $dryRun = null; + + /** + * Do not sync any heads. + */ + protected $noHeads = false; + + /** + * Do not sync any tags. + */ + protected $noTags = false; + + /** + * The splits we found in the heads + */ + protected $splits; + + public function setRemote($str) + { + $this->remote = $str; + } + + public function getRemote() + { + return $this->remote; + } + + public function setHeads($str) + { + $this->heads = explode(',', $str); + } + + public function getHeads() + { + return $this->heads; + } + + public function setTags($str) + { + $this->tags = explode(',', $str); + } + + public function getTags() + { + return $this->tags; + } + + public function setBase($str) + { + $this->base = $str; + } + + public function getBase() + { + return $this->base; + } + + public function setSubIndicatorFile($str) + { + $this->subIndicatorFile = $str; + } + + public function getSubIndicatorFile() + { + return $this->subIndicatorFile; + } + + public function setDryRun($bool) + { + $this->dryRun = (bool) $bool; + } + + public function getDryRun() + { + return $this->dryRun; + } + + public function setNoHeads($bool) + { + $this->noHeads = (bool) $bool; + } + + public function getNoHeads() + { + return $this->noHeads; + } + + public function setNoTags($bool) + { + $this->noTags = (bool) $bool; + } + + public function getNoTags() + { + return $this->noTags; + } + + /** + * GitClient from VersionControl_Git + */ + protected $client = null; + + /** + * The main entry point + */ + public function main() + { + $repo = $this->getRepository(); + if (empty($repo)) { + throw new BuildException('"repository" is a required parameter'); + } + + $remote = $this->getRemote(); + if (empty($remote)) { + throw new BuildException('"remote" is a required parameter'); + } + + chdir($repo); + $this->client = $this->getGitClient(false, $repo); + + // initalized yet? + if (!is_dir('.subsplit')) { + $this->subsplitInit(); + } else { + // update + $this->subsplitUpdate(); + } + + // find all splits based on heads requested + $this->findSplits(); + + // check that GitHub has the repos + $this->verifyRepos(); + + // execute the subsplits + $this->publish(); + } + + public function publish() + { + $this->log('DRY RUN ONLY FOR NOW'); + $base = $this->getBase(); + $base = rtrim($base, '/') . '/'; + $org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + + $splits = array(); + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo']; + } + + $cmd = 'git subsplit publish '; + $cmd .= escapeshellarg(implode(' ', $splits)); + + if ($this->getNoHeads()) { + $cmd .= ' --no-heads'; + } else { + $cmd .= ' --heads='.$head; + } + + if ($this->getNoTags()) { + $cmd .= ' --no-tags'; + } else { + if ($this->getTags()) { + $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags())); + } + } + + passthru($cmd); + } + } + + /** + * Runs `git subsplit update` + */ + public function subsplitUpdate() + { + $repo = $this->getRepository(); + $this->log('git-subsplit update...'); + $cmd = $this->client->getCommand('subsplit'); + $cmd->addArgument('update'); + try { + $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit update failed'. $e); + } + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar update --dev'); + chdir($repo); + } + + /** + * Runs `git subsplit init` based on the remote repository. + */ + public function subsplitInit() + { + $remote = $this->getRemote(); + $cmd = $this->client->getCommand('subsplit'); + $this->log('running git-subsplit init ' . $remote); + + $cmd->setArguments(array( + 'init', + $remote + )); + + try { + $output = $cmd->execute(); + } catch (Exception $e) { + throw new BuildException('git subsplit init failed'. $e); + } + $this->log(trim($output), Project::MSG_INFO); + $repo = $this->getRepository(); + chdir($repo . '/.subsplit'); + passthru('php ../composer.phar install --dev'); + chdir($repo); + } + + /** + * Find the composer.json files using Phing's directory scanner + * + * @return array + */ + protected function findSplits() + { + $this->log("checking heads for subsplits"); + $repo = $this->getRepository(); + $base = $this->getBase(); + + $splits = array(); + $heads = $this->getHeads(); + + if (!empty($base)) { + $base = '/' . ltrim($base, '/'); + } else { + $base = '/'; + } + + chdir($repo . '/.subsplit'); + foreach ($heads as $head) { + $splits[$head] = array(); + + // check each head requested *BEFORE* the actual subtree split command gets it + passthru("git checkout '$head'"); + $ds = new DirectoryScanner(); + $ds->setBasedir($repo . '/.subsplit' . $base); + $ds->setIncludes(array('**/'.$this->subIndicatorFile)); + $ds->scan(); + $files = $ds->getIncludedFiles(); + + // Process the files we found + foreach ($files as $file) { + $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file); + $pkg_json = json_decode($pkg, true); + $name = $pkg_json['name']; + $component = str_replace('/composer.json', '', $file); + // keep this for split cmd + $tmpreponame = explode('/', $name); + $reponame = $tmpreponame[1]; + $splits[$head][$component]['repo'] = $reponame; + $nscomponent = str_replace('/', '\\', $component); + $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description']; + } + } + + // go back to how we found it + passthru("git checkout master"); + chdir($repo); + $this->splits = $splits; + } + + /** + * Based on list of repositories we determined we *should* have, talk + * to GitHub and make sure they're all there. + * + */ + protected function verifyRepos() + { + $this->log('verifying GitHub target repos'); + $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org'); + $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth'); + + if ($github_creds == 'username:password') { + $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1); + return; + } + + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + curl_close($ch); + $repos = json_decode($result, true); + $existing_repos = array(); + + // parse out the repos we found on GitHub + foreach ($repos as $repo) { + $tmpreponame = explode('/', $repo['full_name']); + $reponame = $tmpreponame[1]; + $existing_repos[$reponame] = $repo['description']; + } + + $heads = $this->getHeads(); + foreach ($heads as $head) { + foreach ($this->splits[$head] as $component => $meta) { + + $reponame = $meta['repo']; + + if (!isset($existing_repos[$reponame])) { + $this->log("Creating missing repo $reponame"); + $payload = array( + 'name' => $reponame, + 'description' => $meta['desc'], + 'homepage' => 'http://www.guzzlephp.org/', + 'private' => true, + 'has_issues' => false, + 'has_wiki' => false, + 'has_downloads' => true, + 'auto_init' => false + ); + $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos'); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERPWD, $github_creds); + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); + // change this when we know we can use our bundled CA bundle! + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $result = curl_exec($ch); + echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n"; + curl_close($ch); + } else { + $this->log("Repo $reponame exists", 2); + } + } + } + } +} diff --git a/core/lib/guzzle/guzzle/phpunit.xml.dist b/core/lib/guzzle/guzzle/phpunit.xml.dist new file mode 100644 index 000000000..208fdc08e --- /dev/null +++ b/core/lib/guzzle/guzzle/phpunit.xml.dist @@ -0,0 +1,48 @@ + + + + + + ./tests/Guzzle/Tests + + + + + + + + + + ./src/Guzzle + + ./src/Guzzle + ./src/Guzzle/Common/Exception/GuzzleException.php + ./src/Guzzle/Http/Exception/HttpException.php + ./src/Guzzle/Http/Exception/ServerErrorResponseException.php + ./src/Guzzle/Http/Exception/ClientErrorResponseException.php + ./src/Guzzle/Http/Exception/TooManyRedirectsException.php + ./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php + ./src/Guzzle/Common/Exception/BadMethodCallException.php + ./src/Guzzle/Common/Exception/InvalidArgumentException.php + ./src/Guzzle/Common/Exception/RuntimeException.php + ./src/Guzzle/Common/Exception/UnexpectedValueException.php + ./src/Guzzle/Service/Exception/ClientNotFoundException.php + ./src/Guzzle/Service/Exception/CommandException.php + ./src/Guzzle/Service/Exception/DescriptionBuilderException.php + ./src/Guzzle/Service/Exception/ServiceBuilderException.php + ./src/Guzzle/Service/Exception/ServiceNotFoundException.php + ./src/Guzzle/Service/Exception/ValidationException.php + ./src/Guzzle/Service/Exception/JsonException.php + + + + + diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php new file mode 100644 index 000000000..0625d71c3 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php @@ -0,0 +1,66 @@ +decoratedBatch = $decoratedBatch; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + * @codeCoverageIgnore + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->decoratedBatch, $method), $args); + } + + public function add($item) + { + $this->decoratedBatch->add($item); + + return $this; + } + + public function flush() + { + return $this->decoratedBatch->flush(); + } + + public function isEmpty() + { + return $this->decoratedBatch->isEmpty(); + } + + /** + * Trace the decorators associated with the batch + * + * @return array + */ + public function getDecorators() + { + $found = array($this); + if (method_exists($this->decoratedBatch, 'getDecorators')) { + $found = array_merge($found, $this->decoratedBatch->getDecorators()); + } + + return $found; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php new file mode 100644 index 000000000..4d41c54f8 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php @@ -0,0 +1,92 @@ +transferStrategy = $transferStrategy; + $this->divisionStrategy = $divisionStrategy; + $this->queue = new \SplQueue(); + $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); + $this->dividedBatches = array(); + } + + public function add($item) + { + $this->queue->enqueue($item); + + return $this; + } + + public function flush() + { + $this->createBatches(); + + $items = array(); + foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { + while ($dividedBatch->valid()) { + $batch = $dividedBatch->current(); + $dividedBatch->next(); + try { + $this->transferStrategy->transfer($batch); + $items = array_merge($items, $batch); + } catch (\Exception $e) { + throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); + } + } + // Keep the divided batch down to a minimum in case of a later exception + unset($this->dividedBatches[$batchIndex]); + } + + return $items; + } + + public function isEmpty() + { + return count($this->queue) == 0 && count($this->dividedBatches) == 0; + } + + /** + * Create batches for any queued items + */ + protected function createBatches() + { + if (count($this->queue)) { + if ($batches = $this->divisionStrategy->createBatches($this->queue)) { + // Convert arrays into iterators + if (is_array($batches)) { + $batches = new \ArrayIterator($batches); + } + $this->dividedBatches[] = $batches; + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php new file mode 100644 index 000000000..ea99b4dd0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php @@ -0,0 +1,199 @@ + 'Guzzle\Batch\BatchRequestTransfer', + 'command' => 'Guzzle\Batch\BatchCommandTransfer' + ); + + /** + * Create a new instance of the BatchBuilder + * + * @return BatchBuilder + */ + public static function factory() + { + return new self(); + } + + /** + * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}. + * + * @param $threshold Number of items to allow in the queue before a flush + * + * @return BatchBuilder + */ + public function autoFlushAt($threshold) + { + $this->autoFlush = $threshold; + + return $this; + } + + /** + * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}. + * + * @return BatchBuilder + */ + public function keepHistory() + { + $this->history = true; + + return $this; + } + + /** + * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer + * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator. + * + * @return BatchBuilder + */ + public function bufferExceptions() + { + $this->exceptionBuffering = true; + + return $this; + } + + /** + * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator. + * + * @param mixed $callable Callable function to notify + * + * @return BatchBuilder + * @throws InvalidArgumentException if the argument is not callable + */ + public function notify($callable) + { + $this->afterFlush = $callable; + + return $this; + } + + /** + * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer} + * object as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of requests + * + * @return BatchBuilder + */ + public function transferRequests($batchSize = 50) + { + $className = self::$mapping['request']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Configures the batch to transfer batches commands. Associates as + * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy. + * + * @param int $batchSize Batch size for each batch of commands + * + * @return BatchBuilder + */ + public function transferCommands($batchSize = 50) + { + $className = self::$mapping['command']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Specify the strategy used to divide the queue into an array of batches + * + * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches + * + * @return BatchBuilder + */ + public function createBatchesWith(BatchDivisorInterface $divisorStrategy) + { + $this->divisorStrategy = $divisorStrategy; + + return $this; + } + + /** + * Specify the strategy used to transport the items when flush is called + * + * @param BatchTransferInterface $transferStrategy How items are transferred + * + * @return BatchBuilder + */ + public function transferWith(BatchTransferInterface $transferStrategy) + { + $this->transferStrategy = $transferStrategy; + + return $this; + } + + /** + * Create and return the instantiated batch + * + * @return BatchInterface + * @throws RuntimeException if no transfer strategy has been specified + */ + public function build() + { + if (!$this->transferStrategy) { + throw new RuntimeException('No transfer strategy has been specified'); + } + + if (!$this->divisorStrategy) { + throw new RuntimeException('No divisor strategy has been specified'); + } + + $batch = new Batch($this->transferStrategy, $this->divisorStrategy); + + if ($this->exceptionBuffering) { + $batch = new ExceptionBufferingBatch($batch); + } + + if ($this->afterFlush) { + $batch = new NotifyingBatch($batch, $this->afterFlush); + } + + if ($this->autoFlush) { + $batch = new FlushingBatch($batch, $this->autoFlush); + } + + if ($this->history) { + $batch = new HistoryBatch($batch); + } + + return $batch; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php new file mode 100644 index 000000000..e0a2d9568 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php @@ -0,0 +1,39 @@ +callable = $callable; + $this->context = $context; + } + + public function createBatches(\SplQueue $queue) + { + return call_user_func($this->callable, $queue, $this->context); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php new file mode 100644 index 000000000..9cbf1aba4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php @@ -0,0 +1,40 @@ +callable = $callable; + $this->context = $context; + } + + public function transfer(array $batch) + { + return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php new file mode 100644 index 000000000..d55ac7d1f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php @@ -0,0 +1,75 @@ +batchSize = $batchSize; + } + + /** + * Creates batches by grouping commands by their associated client + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof CommandInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, new \ArrayObject(array($item))); + } else { + $groups[$client]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + // Get the client of the first found command + $client = reset($batch)->getClient(); + + // Keep a list of all commands with invalid clients + $invalid = array_filter($batch, function ($command) use ($client) { + return $command->getClient() !== $client; + }); + + if (!empty($invalid)) { + throw new InconsistentClientTransferException($invalid); + } + + $client->execute($batch); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php new file mode 100644 index 000000000..0214f05f4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php @@ -0,0 +1,18 @@ +batchSize = $batchSize; + } + + /** + * Creates batches of requests by grouping requests by their associated curl multi object. + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + // Create batches by client objects + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof RequestInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, array($item)); + } else { + $current = $groups[$client]; + $current[] = $item; + $groups[$client] = $current; + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize)); + } + + return $batches; + } + + public function transfer(array $batch) + { + if ($batch) { + reset($batch)->getClient()->send($batch); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php new file mode 100644 index 000000000..67f90a581 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php @@ -0,0 +1,47 @@ +size = $size; + } + + /** + * Set the size of each batch + * + * @param int $size Size of each batch + * + * @return BatchSizeDivisor + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Get the size of each batch + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + public function createBatches(\SplQueue $queue) + { + return array_chunk(iterator_to_array($queue, false), $this->size); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php new file mode 100644 index 000000000..2e0b60dad --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php @@ -0,0 +1,16 @@ +batch = $batch; + $this->transferredItems = $transferredItems; + $this->transferStrategy = $transferStrategy; + $this->divisorStrategy = $divisorStrategy; + parent::__construct( + 'Exception encountered while transferring batch: ' . $exception->getMessage(), + $exception->getCode(), + $exception + ); + } + + /** + * Get the batch that we being sent when the exception occurred + * + * @return array + */ + public function getBatch() + { + return $this->batch; + } + + /** + * Get the items transferred at the point in which the exception was encountered + * + * @return array + */ + public function getTransferredItems() + { + return $this->transferredItems; + } + + /** + * Get the transfer strategy + * + * @return TransferStrategy + */ + public function getTransferStrategy() + { + return $this->transferStrategy; + } + + /** + * Get the divisor strategy + * + * @return DivisorStrategy + */ + public function getDivisorStrategy() + { + return $this->divisorStrategy; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php new file mode 100644 index 000000000..d7a892885 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php @@ -0,0 +1,50 @@ +decoratedBatch->isEmpty()) { + try { + $transferredItems = $this->decoratedBatch->flush(); + } catch (BatchTransferException $e) { + $this->exceptions[] = $e; + $transferredItems = $e->getTransferredItems(); + } + $items = array_merge($items, $transferredItems); + } + + return $items; + } + + /** + * Get the buffered exceptions + * + * @return array Array of BatchTransferException objects + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Clear the buffered exceptions + */ + public function clearExceptions() + { + $this->exceptions = array(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php new file mode 100644 index 000000000..367b68427 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php @@ -0,0 +1,60 @@ +threshold = $threshold; + parent::__construct($decoratedBatch); + } + + /** + * Set the auto-flush threshold + * + * @param int $threshold The auto-flush threshold + * + * @return FlushingBatch + */ + public function setThreshold($threshold) + { + $this->threshold = $threshold; + + return $this; + } + + /** + * Get the auto-flush threshold + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + public function add($item) + { + $this->decoratedBatch->add($item); + if (++$this->currentTotal >= $this->threshold) { + $this->currentTotal = 0; + $this->decoratedBatch->flush(); + } + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php new file mode 100644 index 000000000..e345fdc34 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php @@ -0,0 +1,39 @@ +history[] = $item; + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * Get the batch history + * + * @return array + */ + public function getHistory() + { + return $this->history; + } + + /** + * Clear the batch history + */ + public function clearHistory() + { + $this->history = array(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php new file mode 100644 index 000000000..96d04daa8 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php @@ -0,0 +1,38 @@ +callable = $callable; + parent::__construct($decoratedBatch); + } + + public function flush() + { + $items = $this->decoratedBatch->flush(); + call_user_func($this->callable, $items); + + return $items; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json new file mode 100644 index 000000000..12404d381 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json @@ -0,0 +1,31 @@ +{ + "name": "guzzle/batch", + "description": "Guzzle batch component for batching requests, commands, or custom transfers", + "homepage": "http://guzzlephp.org/", + "keywords": ["batch", "HTTP", "REST", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Batch": "" } + }, + "suggest": { + "guzzle/http": "self.version", + "guzzle/service": "self.version" + }, + "target-dir": "Guzzle/Batch", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php new file mode 100644 index 000000000..a5c527167 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php @@ -0,0 +1,21 @@ +cache; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php new file mode 100644 index 000000000..94e623463 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php @@ -0,0 +1,117 @@ +newInstanceArgs($args); + } + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php new file mode 100644 index 000000000..970c9e228 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php @@ -0,0 +1,55 @@ +callables = $callables; + } + + public function contains($id, array $options = null) + { + return call_user_func($this->callables['contains'], $id, $options); + } + + public function delete($id, array $options = null) + { + return call_user_func($this->callables['delete'], $id, $options); + } + + public function fetch($id, array $options = null) + { + return call_user_func($this->callables['fetch'], $id, $options); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php new file mode 100644 index 000000000..321dd6baf --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->contains($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->delete($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->fetch($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($id, $data, $lifeTime); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php new file mode 100644 index 000000000..68bd4af97 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php @@ -0,0 +1,31 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->test($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->remove($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->load($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($data, $id, array(), $lifeTime); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php new file mode 100644 index 000000000..1fc18a555 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php @@ -0,0 +1,41 @@ +cache = $cache; + } + + public function contains($id, array $options = null) + { + return $this->cache->hasItem($id); + } + + public function delete($id, array $options = null) + { + return $this->cache->removeItem($id); + } + + public function fetch($id, array $options = null) + { + return $this->cache->getItem($id); + } + + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->setItem($id, $data); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json new file mode 100644 index 000000000..a5d999bd6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/cache", + "description": "Guzzle cache adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Cache": "" } + }, + "target-dir": "Guzzle/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php new file mode 100644 index 000000000..d1e842b1c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php @@ -0,0 +1,49 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php new file mode 100644 index 000000000..5cb1535d0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php @@ -0,0 +1,403 @@ +data = $data; + } + + /** + * Create a new collection from an array, validate the keys, and add default values where missing + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return self + * @throws InvalidArgumentException if a parameter is missing + */ + public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) + { + $data = $config + $defaults; + + if ($missing = array_diff($required, array_keys($data))) { + throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); + } + + return new self($data); + } + + public function count() + { + return count($this->data); + } + + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + public function toArray() + { + return $this->data; + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = array(); + + return $this; + } + + /** + * Get all or a subset of matching key value pairs + * + * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs + * + * @return array Returns an array of all matching key value pairs + */ + public function getAll(array $keys = null) + { + return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * + * @return mixed|null Value of the key or NULL + */ + public function get($key) + { + return isset($this->data[$key]) ? $this->data[$key] : null; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an + * array and the new value will be pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } elseif (is_array($this->data[$key])) { + $this->data[$key][] = $value; + } else { + $this->data[$key] = array($this->data[$key], $value); + } + + return $this; + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Case insensitive search the keys in the collection + * + * @param string $key Key to search for + * + * @return bool|string Returns false if not found, otherwise returns the key + */ + public function keySearch($key) + { + foreach (array_keys($this->data) as $k) { + if (!strcasecmp($k, $key)) { + return $k; + } + } + + return false; + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data); + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + + return $this; + } + + /** + * Over write key value pairs in this collection with all of the data from an array or collection. + * + * @param array|\Traversable $data Values to override over this config + * + * @return self + */ + public function overwriteWith($data) + { + if (is_array($data)) { + $this->data = $data + $this->data; + } elseif ($data instanceof Collection) { + $this->data = $data->toArray() + $this->data; + } else { + foreach ($data as $key => $value) { + $this->data[$key] = $value; + } + } + + return $this; + } + + /** + * Returns a Collection containing all the elements of the collection after applying the callback function to each + * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a + * modified value + * + * @param \Closure $closure Closure to apply + * @param array $context Context to pass to the closure + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function map(\Closure $closure, array $context = array(), $static = true) + { + $collection = $static ? new static() : new self(); + foreach ($this as $key => $value) { + $collection->add($key, $closure($key, $value, $context)); + } + + return $collection; + } + + /** + * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns + * true, the current value from input is returned into the result Collection. The Closure must accept three + * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. + * + * @param \Closure $closure Closure evaluation function + * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection + * + * @return Collection + */ + public function filter(\Closure $closure, $static = true) + { + $collection = ($static) ? new static() : new self(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection->add($key, $value); + } + } + + return $collection; + } + + public function offsetExists($offset) + { + return isset($this->data[$offset]); + } + + public function offsetGet($offset) + { + return isset($this->data[$offset]) ? $this->data[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->data[$offset] = $value; + } + + public function offsetUnset($offset) + { + unset($this->data[$offset]); + } + + /** + * Set a value into a nested array key. Keys will be created as needed to set the value. + * + * @param string $path Path to set + * @param mixed $value Value to set at the key + * + * @return self + * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value + */ + public function setPath($path, $value) + { + $current =& $this->data; + $queue = explode('/', $path); + while (null !== ($key = array_shift($queue))) { + if (!is_array($current)) { + throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); + } elseif (!$queue) { + $current[$key] = $value; + } elseif (isset($current[$key])) { + $current =& $current[$key]; + } else { + $current[$key] = array(); + $current =& $current[$key]; + } + } + + return $this; + } + + /** + * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) + * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This + * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. + * + * @param string $path Path to traverse and retrieve a value from + * @param string $separator Character used to add depth to the search + * @param mixed $data Optional data to descend into (used when wildcards are encountered) + * + * @return mixed|null + */ + public function getPath($path, $separator = '/', $data = null) + { + if ($data === null) { + $data =& $this->data; + } + + $path = is_array($path) ? $path : explode($separator, $path); + while (null !== ($part = array_shift($path))) { + if (!is_array($data)) { + return null; + } elseif (isset($data[$part])) { + $data =& $data[$part]; + } elseif ($part != '*') { + return null; + } else { + // Perform a wildcard search by diverging and merging paths + $result = array(); + foreach ($data as $value) { + if (!$path) { + $result = array_merge_recursive($result, (array) $value); + } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { + $result = array_merge_recursive($result, (array) $test); + } + } + return $result; + } + } + + return $data; + } + + /** + * Inject configuration settings into an input string + * + * @param string $input Input to inject + * + * @return string + * @deprecated + */ + public function inject($input) + { + Version::warn(__METHOD__ . ' is deprecated'); + $replace = array(); + foreach ($this->data as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + return strtr($input, $replace); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php new file mode 100644 index 000000000..fad76a9b8 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php @@ -0,0 +1,52 @@ +context = $context; + } + + public function getIterator() + { + return new \ArrayIterator($this->context); + } + + public function offsetGet($offset) + { + return isset($this->context[$offset]) ? $this->context[$offset] : null; + } + + public function offsetSet($offset, $value) + { + $this->context[$offset] = $value; + } + + public function offsetExists($offset) + { + return isset($this->context[$offset]); + } + + public function offsetUnset($offset) + { + unset($this->context[$offset]); + } + + public function toArray() + { + return $this->context; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php new file mode 100644 index 000000000..08d1c7256 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php @@ -0,0 +1,5 @@ +shortMessage = $message; + } + + /** + * Set all of the exceptions + * + * @param array $exceptions Array of exceptions + * + * @return self + */ + public function setExceptions(array $exceptions) + { + $this->exceptions = array(); + foreach ($exceptions as $exception) { + $this->add($exception); + } + + return $this; + } + + /** + * Add exceptions to the collection + * + * @param ExceptionCollection|\Exception $e Exception to add + * + * @return ExceptionCollection; + */ + public function add($e) + { + $this->exceptions[] = $e; + if ($this->message) { + $this->message .= "\n"; + } + + $this->message .= $this->getExceptionMessage($e, 0); + + return $this; + } + + /** + * Get the total number of request exceptions + * + * @return int + */ + public function count() + { + return count($this->exceptions); + } + + /** + * Allows array-like iteration over the request exceptions + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->exceptions); + } + + /** + * Get the first exception in the collection + * + * @return \Exception + */ + public function getFirst() + { + return $this->exceptions ? $this->exceptions[0] : null; + } + + private function getExceptionMessage(\Exception $e, $depth = 0) + { + static $sp = ' '; + $prefix = $depth ? str_repeat($sp, $depth) : ''; + $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; + + if ($e instanceof self) { + if ($e->shortMessage) { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; + } + foreach ($e as $ee) { + $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); + } + } else { + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; + $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; + } + + return str_replace(getcwd(), '.', $message); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php new file mode 100644 index 000000000..458e6f2ea --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php @@ -0,0 +1,8 @@ +=5.3.2", + "symfony/event-dispatcher": ">=2.1" + }, + "autoload": { + "psr-0": { "Guzzle\\Common": "" } + }, + "target-dir": "Guzzle/Common", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php new file mode 100644 index 000000000..5005a887c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php @@ -0,0 +1,221 @@ +body = $body; + } + + public function __toString() + { + return (string) $this->body; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + */ + public function __call($method, array $args) + { + return call_user_func_array(array($this->body, $method), $args); + } + + public function close() + { + return $this->body->close(); + } + + public function setRewindFunction($callable) + { + $this->body->setRewindFunction($callable); + + return $this; + } + + public function rewind() + { + return $this->body->rewind(); + } + + public function compress($filter = 'zlib.deflate') + { + return $this->body->compress($filter); + } + + public function uncompress($filter = 'zlib.inflate') + { + return $this->body->uncompress($filter); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->body->getContentType(); + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + $hash = Stream::getHash($this, 'md5', $rawOutput); + + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } + + public function getContentEncoding() + { + return $this->body->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->body->getMetaData($key); + } + + public function getStream() + { + return $this->body->getStream(); + } + + public function setStream($stream, $size = 0) + { + $this->body->setStream($stream, $size); + + return $this; + } + + public function detachStream() + { + $this->body->detachStream(); + + return $this; + } + + public function getWrapper() + { + return $this->body->getWrapper(); + } + + public function getWrapperData() + { + return $this->body->getWrapperData(); + } + + public function getStreamType() + { + return $this->body->getStreamType(); + } + + public function getUri() + { + return $this->body->getUri(); + } + + public function getSize() + { + return $this->body->getSize(); + } + + public function isReadable() + { + return $this->body->isReadable(); + } + + public function isRepeatable() + { + return $this->isSeekable() && $this->isReadable(); + } + + public function isWritable() + { + return $this->body->isWritable(); + } + + public function isConsumed() + { + return $this->body->isConsumed(); + } + + /** + * Alias of isConsumed() + * {@inheritdoc} + */ + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->body->isLocal(); + } + + public function isSeekable() + { + return $this->body->isSeekable(); + } + + public function setSize($size) + { + $this->body->setSize($size); + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->body->seek($offset, $whence); + } + + public function read($length) + { + return $this->body->read($length); + } + + public function write($string) + { + return $this->body->write($string); + } + + public function readLine($maxLength = null) + { + return $this->body->readLine($maxLength); + } + + public function ftell() + { + return $this->body->ftell(); + } + + public function getCustomData($key) + { + return $this->body->getCustomData($key); + } + + public function setCustomData($key, $value) + { + $this->body->setCustomData($key, $value); + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php new file mode 100644 index 000000000..c65c13650 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php @@ -0,0 +1,229 @@ +remoteStream = $body; + $this->body = new EntityBody(fopen('php://temp', 'r+')); + } + + /** + * Will give the contents of the buffer followed by the exhausted remote stream. + * + * Warning: Loads the entire stream into memory + * + * @return string + */ + public function __toString() + { + $pos = $this->ftell(); + $this->rewind(); + + $str = ''; + while (!$this->isConsumed()) { + $str .= $this->read(16384); + } + + $this->seek($pos); + + return $str; + } + + public function getSize() + { + return max($this->body->getSize(), $this->remoteStream->getSize()); + } + + /** + * {@inheritdoc} + * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream + */ + public function seek($offset, $whence = SEEK_SET) + { + if ($whence == SEEK_SET) { + $byte = $offset; + } elseif ($whence == SEEK_CUR) { + $byte = $offset + $this->ftell(); + } else { + throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); + } + + // You cannot skip ahead past where you've read from the remote stream + if ($byte > $this->body->getSize()) { + throw new RuntimeException( + "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" + ); + } + + return $this->body->seek($byte); + } + + public function rewind() + { + return $this->seek(0); + } + + /** + * Does not support custom rewind functions + * + * @throws RuntimeException + */ + public function setRewindFunction($callable) + { + throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); + } + + public function read($length) + { + // Perform a regular read on any previously read data from the buffer + $data = $this->body->read($length); + $remaining = $length - strlen($data); + + // More data was requested so read from the remote stream + if ($remaining) { + // If data was written to the buffer in a position that would have been filled from the remote stream, + // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This + // mimics the behavior of other PHP stream wrappers. + $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); + + if ($this->skipReadBytes) { + $len = strlen($remoteData); + $remoteData = substr($remoteData, $this->skipReadBytes); + $this->skipReadBytes = max(0, $this->skipReadBytes - $len); + } + + $data .= $remoteData; + $this->body->write($remoteData); + } + + return $data; + } + + public function write($string) + { + // When appending to the end of the currently read stream, you'll want to skip bytes from being read from + // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. + $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); + if ($overflow > 0) { + $this->skipReadBytes += $overflow; + } + + return $this->body->write($string); + } + + /** + * {@inheritdoc} + * @link http://php.net/manual/en/function.fgets.php + */ + public function readLine($maxLength = null) + { + $buffer = ''; + $size = 0; + while (!$this->isConsumed()) { + $byte = $this->read(1); + $buffer .= $byte; + // Break when a new line is found or the max length - 1 is reached + if ($byte == PHP_EOL || ++$size == $maxLength - 1) { + break; + } + } + + return $buffer; + } + + public function isConsumed() + { + return $this->body->isConsumed() && $this->remoteStream->isConsumed(); + } + + /** + * Close both the remote stream and buffer stream + */ + public function close() + { + return $this->remoteStream->close() && $this->body->close(); + } + + public function setStream($stream, $size = 0) + { + $this->remoteStream->setStream($stream, $size); + } + + public function getContentType() + { + return $this->remoteStream->getContentType(); + } + + public function getContentEncoding() + { + return $this->remoteStream->getContentEncoding(); + } + + public function getMetaData($key = null) + { + return $this->remoteStream->getMetaData($key); + } + + public function getStream() + { + return $this->remoteStream->getStream(); + } + + public function getWrapper() + { + return $this->remoteStream->getWrapper(); + } + + public function getWrapperData() + { + return $this->remoteStream->getWrapperData(); + } + + public function getStreamType() + { + return $this->remoteStream->getStreamType(); + } + + public function getUri() + { + return $this->remoteStream->getUri(); + } + + /** + * Always retrieve custom data from the remote stream + * {@inheritdoc} + */ + public function getCustomData($key) + { + return $this->remoteStream->getCustomData($key); + } + + /** + * Always set custom data on the remote stream + * {@inheritdoc} + */ + public function setCustomData($key, $value) + { + $this->remoteStream->setCustomData($key, $value); + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php new file mode 100644 index 000000000..22df2ec6e --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php @@ -0,0 +1,490 @@ +setConfig($config ?: new Collection()); + $this->initSsl(); + $this->setBaseUrl($baseUrl); + $this->defaultHeaders = new Collection(); + $this->setRequestFactory(RequestFactory::getInstance()); + $this->userAgent = $this->getDefaultUserAgent(); + if (!$this->config[self::DISABLE_REDIRECTS]) { + $this->addSubscriber(new RedirectPlugin()); + } + } + + final public function setConfig($config) + { + if ($config instanceof Collection) { + $this->config = $config; + } elseif (is_array($config)) { + $this->config = new Collection($config); + } else { + throw new InvalidArgumentException('Config must be an array or Collection'); + } + + return $this; + } + + final public function getConfig($key = false) + { + return $key ? $this->config[$key] : $this->config; + } + + /** + * Set a default request option on the client that will be used as a default for each request + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * @param mixed $value Value to set + * + * @return $this + */ + public function setDefaultOption($keyOrPath, $value) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + $this->config->setPath($keyOrPath, $value); + + return $this; + } + + /** + * Retrieve a default request option from the client + * + * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) + * + * @return mixed|null + */ + public function getDefaultOption($keyOrPath) + { + $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; + + return $this->config->getPath($keyOrPath); + } + + final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) + { + $opts = $this->config[self::CURL_OPTIONS] ?: array(); + + if ($certificateAuthority === true) { + // use bundled CA bundle, set secure defaults + $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; + $opts[CURLOPT_SSL_VERIFYPEER] = true; + $opts[CURLOPT_SSL_VERIFYHOST] = 2; + } elseif ($certificateAuthority === false) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_SSL_VERIFYPEER] = false; + $opts[CURLOPT_SSL_VERIFYHOST] = 0; + } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { + throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); + } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { + throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); + } else { + $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; + $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; + if (is_file($certificateAuthority)) { + unset($opts[CURLOPT_CAPATH]); + $opts[CURLOPT_CAINFO] = $certificateAuthority; + } elseif (is_dir($certificateAuthority)) { + unset($opts[CURLOPT_CAINFO]); + $opts[CURLOPT_CAPATH] = $certificateAuthority; + } else { + throw new RuntimeException( + 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority + ); + } + } + + $this->config->set(self::CURL_OPTIONS, $opts); + + return $this; + } + + public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) + { + if (!$uri) { + $url = $this->getBaseUrl(); + } else { + if (!is_array($uri)) { + $templateVars = null; + } else { + list($uri, $templateVars) = $uri; + } + if (strpos($uri, '://')) { + // Use absolute URLs as-is + $url = $this->expandTemplate($uri, $templateVars); + } else { + $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); + } + } + + // If default headers are provided, then merge them under any explicitly provided headers for the request + if (count($this->defaultHeaders)) { + if (!$headers) { + $headers = $this->defaultHeaders->toArray(); + } elseif (is_array($headers)) { + $headers += $this->defaultHeaders->toArray(); + } elseif ($headers instanceof Collection) { + $headers = $headers->toArray() + $this->defaultHeaders->toArray(); + } + } + + return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); + } + + public function getBaseUrl($expand = true) + { + return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; + } + + public function setBaseUrl($url) + { + $this->baseUrl = $url; + + return $this; + } + + public function setUserAgent($userAgent, $includeDefault = false) + { + if ($includeDefault) { + $userAgent .= ' ' . $this->getDefaultUserAgent(); + } + $this->userAgent = $userAgent; + + return $this; + } + + /** + * Get the default User-Agent string to use with Guzzle + * + * @return string + */ + public function getDefaultUserAgent() + { + return 'Guzzle/' . Version::VERSION + . ' curl/' . CurlVersion::getInstance()->get('version') + . ' PHP/' . PHP_VERSION; + } + + public function get($uri = null, $headers = null, $options = array()) + { + // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded + return is_array($options) + ? $this->createRequest('GET', $uri, $headers, null, $options) + : $this->createRequest('GET', $uri, $headers, $options); + } + + public function head($uri = null, $headers = null, array $options = array()) + { + return $this->createRequest('HEAD', $uri, $headers, null, $options); + } + + public function delete($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('DELETE', $uri, $headers, $body, $options); + } + + public function put($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PUT', $uri, $headers, $body, $options); + } + + public function patch($uri = null, $headers = null, $body = null, array $options = array()) + { + return $this->createRequest('PATCH', $uri, $headers, $body, $options); + } + + public function post($uri = null, $headers = null, $postBody = null, array $options = array()) + { + return $this->createRequest('POST', $uri, $headers, $postBody, $options); + } + + public function options($uri = null, array $options = array()) + { + return $this->createRequest('OPTIONS', $uri, $options); + } + + public function send($requests) + { + if (!($requests instanceof RequestInterface)) { + return $this->sendMultiple($requests); + } + + try { + /** @var $requests RequestInterface */ + $this->getCurlMulti()->add($requests)->send(); + return $requests->getResponse(); + } catch (ExceptionCollection $e) { + throw $e->getFirst(); + } + } + + /** + * Set a curl multi object to be used internally by the client for transferring requests. + * + * @param CurlMultiInterface $curlMulti Multi object + * + * @return self + */ + public function setCurlMulti(CurlMultiInterface $curlMulti) + { + $this->curlMulti = $curlMulti; + + return $this; + } + + /** + * @return CurlMultiInterface|CurlMultiProxy + */ + public function getCurlMulti() + { + if (!$this->curlMulti) { + $this->curlMulti = new CurlMultiProxy( + self::MAX_HANDLES, + $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT + ); + } + + return $this->curlMulti; + } + + public function setRequestFactory(RequestFactoryInterface $factory) + { + $this->requestFactory = $factory; + + return $this; + } + + /** + * Set the URI template expander to use with the client + * + * @param UriTemplateInterface $uriTemplate URI template expander + * + * @return self + */ + public function setUriTemplate(UriTemplateInterface $uriTemplate) + { + $this->uriTemplate = $uriTemplate; + + return $this; + } + + /** + * Expand a URI template while merging client config settings into the template variables + * + * @param string $template Template to expand + * @param array $variables Variables to inject + * + * @return string + */ + protected function expandTemplate($template, array $variables = null) + { + $expansionVars = $this->getConfig()->toArray(); + if ($variables) { + $expansionVars = $variables + $expansionVars; + } + + return $this->getUriTemplate()->expand($template, $expansionVars); + } + + /** + * Get the URI template expander used by the client + * + * @return UriTemplateInterface + */ + protected function getUriTemplate() + { + if (!$this->uriTemplate) { + $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); + } + + return $this->uriTemplate; + } + + /** + * Send multiple requests in parallel + * + * @param array $requests Array of RequestInterface objects + * + * @return array Returns an array of Response objects + */ + protected function sendMultiple(array $requests) + { + $curlMulti = $this->getCurlMulti(); + foreach ($requests as $request) { + $curlMulti->add($request); + } + $curlMulti->send(); + + /** @var $request RequestInterface */ + $result = array(); + foreach ($requests as $request) { + $result[] = $request->getResponse(); + } + + return $result; + } + + /** + * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. + * + * @param RequestInterface $request Request to prepare for the client + * @param array $options Options to apply to the request + * + * @return RequestInterface + */ + protected function prepareRequest(RequestInterface $request, array $options = array()) + { + $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); + + if ($curl = $this->config[self::CURL_OPTIONS]) { + $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); + } + + if ($params = $this->config[self::REQUEST_PARAMS]) { + Version::warn('request.params is deprecated. Use request.options to add default request options.'); + $request->getParams()->overwriteWith($params); + } + + if ($this->userAgent && !$request->hasHeader('User-Agent')) { + $request->setHeader('User-Agent', $this->userAgent); + } + + if ($defaults = $this->config[self::REQUEST_OPTIONS]) { + $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); + } + + if ($options) { + $this->requestFactory->applyOptions($request, $options); + } + + $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); + + return $request; + } + + /** + * Initializes SSL settings + */ + protected function initSsl() + { + $authority = $this->config[self::SSL_CERT_AUTHORITY]; + + if ($authority === 'system') { + return; + } + + if ($authority === null) { + $authority = true; + } + + if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { + $authority = sys_get_temp_dir() . '/guzzle-cacert.pem'; + } + + $this->setSslVerification($authority); + } + + /** + * @deprecated + */ + public function getDefaultHeaders() + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); + return $this->defaultHeaders; + } + + /** + * @deprecated + */ + public function setDefaultHeaders($headers) + { + Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); + if ($headers instanceof Collection) { + $this->defaultHeaders = $headers; + } elseif (is_array($headers)) { + $this->defaultHeaders = new Collection($headers); + } else { + throw new InvalidArgumentException('Headers must be an array or Collection'); + } + + return $this; + } + + /** + * @deprecated + */ + public function preparePharCacert($md5Check = true) + { + return sys_get_temp_dir() . '/guzzle-cacert.pem'; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php new file mode 100644 index 000000000..10e4de2ab --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php @@ -0,0 +1,223 @@ +getCurlOptions(); + $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); + $tempContentLength = null; + $method = $request->getMethod(); + $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); + + // Prepare url + $url = (string)$request->getUrl(); + if(($pos = strpos($url, '#')) !== false ){ + // strip fragment from url + $url = substr($url, 0, $pos); + } + + // Array of default cURL options. + $curlOptions = array( + CURLOPT_URL => $url, + CURLOPT_CONNECTTIMEOUT => 150, + CURLOPT_RETURNTRANSFER => false, + CURLOPT_HEADER => false, + CURLOPT_PORT => $request->getPort(), + CURLOPT_HTTPHEADER => array(), + CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' + ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + // Verifies the authenticity of the peer's certificate + CURLOPT_SSL_VERIFYPEER => 1, + // Certificate must indicate that the server is the server to which you meant to connect + CURLOPT_SSL_VERIFYHOST => 2 + ); + + if (defined('CURLOPT_PROTOCOLS')) { + // Allow only HTTP and HTTPS protocols + $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; + } + + // Add CURLOPT_ENCODING if Accept-Encoding header is provided + if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { + $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; + // Let cURL set the Accept-Encoding header, prevents duplicate values + $request->removeHeader('Accept-Encoding'); + } + + // Enable curl debug information if the 'debug' param was set + if ($requestCurlOptions->get('debug')) { + $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); + // @codeCoverageIgnoreStart + if (false === $curlOptions[CURLOPT_STDERR]) { + throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); + } + // @codeCoverageIgnoreEnd + $curlOptions[CURLOPT_VERBOSE] = true; + } + + // Specify settings according to the HTTP method + if ($method == 'GET') { + $curlOptions[CURLOPT_HTTPGET] = true; + } elseif ($method == 'HEAD') { + $curlOptions[CURLOPT_NOBODY] = true; + // HEAD requests do not use a write function + unset($curlOptions[CURLOPT_WRITEFUNCTION]); + } elseif (!($request instanceof EntityEnclosingRequest)) { + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + } else { + + $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; + + // Handle sending raw bodies in a request + if ($request->getBody()) { + // You can send the body as a string using curl's CURLOPT_POSTFIELDS + if ($bodyAsString) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); + // Allow curl to add the Content-Length for us to account for the times when + // POST redirects are followed by GET requests + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + } + // Remove the curl generated Content-Type header if none was set manually + if (!$request->hasHeader('Content-Type')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; + } + } else { + $curlOptions[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + if ($tempContentLength = $request->getHeader('Content-Length')) { + $tempContentLength = (int) (string) $tempContentLength; + $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; + } + // Add a callback for curl to read data to send with the request only if a body was specified + $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); + // Attempt to seek to the start of the stream + $request->getBody()->seek(0); + } + + } else { + + // Special handling for POST specific fields and files + $postFields = false; + if (count($request->getPostFiles())) { + $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); + foreach ($request->getPostFiles() as $key => $data) { + $prefixKeys = count($data) > 1; + foreach ($data as $index => $file) { + // Allow multiple files in the same key + $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; + $postFields[$fieldKey] = $file->getCurlValue(); + } + } + } elseif (count($request->getPostFields())) { + $postFields = (string) $request->getPostFields()->useUrlEncoding(true); + } + + if ($postFields !== false) { + if ($method == 'POST') { + unset($curlOptions[CURLOPT_CUSTOMREQUEST]); + $curlOptions[CURLOPT_POST] = true; + } + $curlOptions[CURLOPT_POSTFIELDS] = $postFields; + $request->removeHeader('Content-Length'); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + // If a Content-Length header was specified but we want to allow curl to set one for us + if (null !== $tempContentLength) { + $request->removeHeader('Content-Length'); + } + + // Set custom cURL options + foreach ($requestCurlOptions->toArray() as $key => $value) { + if (is_numeric($key)) { + $curlOptions[$key] = $value; + } + } + + // Do not set an Accept header by default + if (!isset($curlOptions[CURLOPT_ENCODING])) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; + } + + // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. + foreach ($request->getHeaderLines() as $line) { + $curlOptions[CURLOPT_HTTPHEADER][] = $line; + } + + // Add the content-length header back if it was temporarily removed + if ($tempContentLength) { + $request->setHeader('Content-Length', $tempContentLength); + } + + // Apply the options to a new cURL handle. + $handle = curl_init(); + + // Enable the progress function if the 'progress' param was set + if ($requestCurlOptions->get('progress')) { + // Wrap the function in a function that provides the curl handle to the mediator's progress function + // Using this rather than injecting the handle into the mediator prevents a circular reference + $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { + $args = func_get_args(); + $args[] = $handle; + + // PHP 5.5 pushed the handle onto the start of the args + if (is_resource($args[0])) { + array_shift($args); + } + + call_user_func_array(array($mediator, 'progress'), $args); + }; + $curlOptions[CURLOPT_NOPROGRESS] = false; + } + + curl_setopt_array($handle, $curlOptions); + + return new static($handle, $curlOptions); + } + + /** + * Construct a new CurlHandle object that wraps a cURL handle + * + * @param resource $handle Configured cURL handle resource + * @param Collection|array $options Curl options to use with the handle + * + * @throws InvalidArgumentException + */ + public function __construct($handle, $options) + { + if (!is_resource($handle)) { + throw new InvalidArgumentException('Invalid handle provided'); + } + if (is_array($options)) { + $this->options = new Collection($options); + } elseif ($options instanceof Collection) { + $this->options = $options; + } else { + throw new InvalidArgumentException('Expected array or Collection'); + } + $this->handle = $handle; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->close(); + } + + /** + * Close the curl handle + */ + public function close() + { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + $this->handle = null; + } + + /** + * Check if the handle is available and still OK + * + * @return bool + */ + public function isAvailable() + { + return is_resource($this->handle); + } + + /** + * Get the last error that occurred on the cURL handle + * + * @return string + */ + public function getError() + { + return $this->isAvailable() ? curl_error($this->handle) : ''; + } + + /** + * Get the last error number that occurred on the cURL handle + * + * @return int + */ + public function getErrorNo() + { + if ($this->errorNo) { + return $this->errorNo; + } + + return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; + } + + /** + * Set the curl error number + * + * @param int $error Error number to set + * + * @return CurlHandle + */ + public function setErrorNo($error) + { + $this->errorNo = $error; + + return $this; + } + + /** + * Get cURL curl_getinfo data + * + * @param int $option Option to retrieve. Pass null to retrieve all data as an array. + * + * @return array|mixed + */ + public function getInfo($option = null) + { + if (!is_resource($this->handle)) { + return null; + } + + if (null !== $option) { + return curl_getinfo($this->handle, $option) ?: null; + } + + return curl_getinfo($this->handle) ?: array(); + } + + /** + * Get the stderr output + * + * @param bool $asResource Set to TRUE to get an fopen resource + * + * @return string|resource|null + */ + public function getStderr($asResource = false) + { + $stderr = $this->getOptions()->get(CURLOPT_STDERR); + if (!$stderr) { + return null; + } + + if ($asResource) { + return $stderr; + } + + fseek($stderr, 0); + $e = stream_get_contents($stderr); + fseek($stderr, 0, SEEK_END); + + return $e; + } + + /** + * Get the URL that this handle is connecting to + * + * @return Url + */ + public function getUrl() + { + return Url::factory($this->options->get(CURLOPT_URL)); + } + + /** + * Get the wrapped curl handle + * + * @return resource|null Returns the cURL handle or null if it was closed + */ + public function getHandle() + { + return $this->isAvailable() ? $this->handle : null; + } + + /** + * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl + * handle after it is created. + * + * @return Collection + */ + public function getOptions() + { + return $this->options; + } + + /** + * Update a request based on the log messages of the CurlHandle + * + * @param RequestInterface $request Request to update + */ + public function updateRequestFromTransfer(RequestInterface $request) + { + if (!$request->getResponse()) { + return; + } + + // Update the transfer stats of the response + $request->getResponse()->setInfo($this->getInfo()); + + if (!$log = $this->getStderr(true)) { + return; + } + + // Parse the cURL stderr output for outgoing requests + $headers = ''; + fseek($log, 0); + while (($line = fgets($log)) !== false) { + if ($line && $line[0] == '>') { + $headers = substr(trim($line), 2) . "\r\n"; + while (($line = fgets($log)) !== false) { + if ($line[0] == '*' || $line[0] == '<') { + break; + } else { + $headers .= trim($line) . "\r\n"; + } + } + } + } + + // Add request headers to the request exactly as they were sent + if ($headers) { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); + if (!empty($parsed['headers'])) { + $request->setHeaders(array()); + foreach ($parsed['headers'] as $name => $value) { + $request->setHeader($name, $value); + } + } + if (!empty($parsed['version'])) { + $request->setProtocolVersion($parsed['version']); + } + } + } + + /** + * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere + * + * @param array|Collection $config The configuration we want to parse + * + * @return array + */ + public static function parseCurlConfig($config) + { + $curlOptions = array(); + foreach ($config as $key => $value) { + if (is_string($key) && defined($key)) { + // Convert constants represented as string to constant int values + $key = constant($key); + } + if (is_string($value) && defined($value)) { + $value = constant($value); + } + $curlOptions[$key] = $value; + } + + return $curlOptions; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php new file mode 100644 index 000000000..e8301be34 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php @@ -0,0 +1,367 @@ + array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') + ); + + /** @var float */ + protected $selectTimeout; + + public function __construct($selectTimeout = 1.0) + { + $this->selectTimeout = $selectTimeout; + $this->multiHandle = curl_multi_init(); + // @codeCoverageIgnoreStart + if ($this->multiHandle === false) { + throw new CurlException('Unable to create multi handle'); + } + // @codeCoverageIgnoreEnd + $this->reset(); + } + + public function __destruct() + { + if (is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + } + + public function add(RequestInterface $request) + { + $this->requests[] = $request; + // If requests are currently transferring and this is async, then the + // request must be prepared now as the send() method is not called. + $this->beforeSend($request); + $this->dispatch(self::ADD_REQUEST, array('request' => $request)); + + return $this; + } + + public function all() + { + return $this->requests; + } + + public function remove(RequestInterface $request) + { + $this->removeHandle($request); + if (($index = array_search($request, $this->requests, true)) !== false) { + $request = $this->requests[$index]; + unset($this->requests[$index]); + $this->requests = array_values($this->requests); + $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); + return true; + } + + return false; + } + + public function reset($hard = false) + { + // Remove each request + if ($this->requests) { + foreach ($this->requests as $request) { + $this->remove($request); + } + } + + $this->handles = new \SplObjectStorage(); + $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); + } + + public function send() + { + $this->perform(); + $exceptions = $this->exceptions; + $successful = $this->successful; + $this->reset(); + + if ($exceptions) { + $this->throwMultiException($exceptions, $successful); + } + } + + public function count() + { + return count($this->requests); + } + + /** + * Build and throw a MultiTransferException + * + * @param array $exceptions Exceptions encountered + * @param array $successful Successful requests + * @throws MultiTransferException + */ + protected function throwMultiException(array $exceptions, array $successful) + { + $multiException = new MultiTransferException('Errors during multi transfer'); + + while ($e = array_shift($exceptions)) { + $multiException->addFailedRequestWithException($e['request'], $e['exception']); + } + + // Add successful requests + foreach ($successful as $request) { + if (!$multiException->containsRequest($request)) { + $multiException->addSuccessfulRequest($request); + } + } + + throw $multiException; + } + + /** + * Prepare for sending + * + * @param RequestInterface $request Request to prepare + * @throws \Exception on error preparing the request + */ + protected function beforeSend(RequestInterface $request) + { + try { + $state = $request->setState(RequestInterface::STATE_TRANSFER); + if ($state == RequestInterface::STATE_TRANSFER) { + // Add the request curl handle to the multi handle + $handle = $this->createCurlHandle($request)->getHandle(); + $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $handle)); + } else { + // Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin) + $this->remove($request); + if ($state == RequestInterface::STATE_COMPLETE) { + $this->successful[] = $request; + } + } + } catch (\Exception $e) { + // Queue the exception to be thrown when sent + $this->removeErroredRequest($request, $e); + } + } + + /** + * Create a curl handle for a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle + */ + protected function createCurlHandle(RequestInterface $request) + { + $wrapper = CurlHandle::factory($request); + $this->handles[$request] = $wrapper; + $this->resourceHash[(int) $wrapper->getHandle()] = $request; + + return $wrapper; + } + + /** + * Get the data from the multi handle + */ + protected function perform() + { + $event = new Event(array('curl_multi' => $this)); + + while ($this->requests) { + // Notify each request as polling + $blocking = $total = 0; + foreach ($this->requests as $request) { + ++$total; + $event['request'] = $request; + $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); + // The blocking variable just has to be non-falsey to block the loop + if ($request->getParams()->hasKey(self::BLOCKING)) { + ++$blocking; + } + } + if ($blocking == $total) { + // Sleep to prevent eating CPU because no requests are actually pending a select call + usleep(500); + } else { + $this->executeHandles(); + } + } + } + + /** + * Execute and select curl handles + */ + private function executeHandles() + { + // The first curl_multi_select often times out no matter what, but is usually required for fast transfers + $selectTimeout = 0.001; + $active = false; + do { + while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); + $this->checkCurlResult($mrc); + $this->processMessages(); + if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { + // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 + usleep(150); + } + $selectTimeout = $this->selectTimeout; + } while ($active); + } + + /** + * Process any received curl multi messages + */ + private function processMessages() + { + while ($done = curl_multi_info_read($this->multiHandle)) { + $request = $this->resourceHash[(int) $done['handle']]; + try { + $this->processResponse($request, $this->handles[$request], $done); + $this->successful[] = $request; + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + } + } + + /** + * Remove a request that encountered an exception + * + * @param RequestInterface $request Request to remove + * @param \Exception $e Exception encountered + */ + protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) + { + $this->exceptions[] = array('request' => $request, 'exception' => $e); + $this->remove($request); + $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); + } + + /** + * Check for errors and fix headers of a request based on a curl response + * + * @param RequestInterface $request Request to process + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @throws CurlException on Curl error + */ + protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) + { + // Set the transfer stats on the response + $handle->updateRequestFromTransfer($request); + // Check if a cURL exception occurred, and if so, notify things + $curlException = $this->isCurlException($request, $handle, $curl); + + // Always remove completed curl handles. They can be added back again + // via events if needed (e.g. ExponentialBackoffPlugin) + $this->removeHandle($request); + + if (!$curlException) { + $state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle)); + // Only remove the request if it wasn't resent as a result of the state change + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + } else { + // Set the state of the request to an error + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); + // Allow things to ignore the error if possible + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + // The error was not handled, so fail + if ($state == RequestInterface::STATE_ERROR) { + /** @var CurlException $curlException */ + throw $curlException; + } + } + } + + /** + * Remove a curl handle from the curl multi object + * + * @param RequestInterface $request Request that owns the handle + */ + protected function removeHandle(RequestInterface $request) + { + if (isset($this->handles[$request])) { + $handle = $this->handles[$request]; + curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); + unset($this->handles[$request]); + unset($this->resourceHash[(int) $handle->getHandle()]); + $handle->close(); + } + } + + /** + * Check if a cURL transfer resulted in what should be an exception + * + * @param RequestInterface $request Request to check + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @return CurlException|bool + */ + private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) + { + if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { + return false; + } + + $handle->setErrorNo($curl['result']); + $e = new CurlException(sprintf('[curl] %s: %s [url] %s', + $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); + $e->setCurlHandle($handle) + ->setRequest($request) + ->setCurlInfo($handle->getInfo()) + ->setError($handle->getError(), $handle->getErrorNo()); + + return $e; + } + + /** + * Throw an exception for a cURL multi response if needed + * + * @param int $code Curl response code + * @throws CurlException + */ + private function checkCurlResult($code) + { + if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { + throw new CurlException(isset($this->multiErrors[$code]) + ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" + : 'Unexpected cURL error: ' . $code + ); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php new file mode 100644 index 000000000..0ead75735 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php @@ -0,0 +1,58 @@ +maxHandles = $maxHandles; + $this->selectTimeout = $selectTimeout; + // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. + // These two statements autoload classes before a system runs out of file descriptors so that you can get back + // valuable error messages if you run out. + class_exists('Guzzle\Http\Message\Response'); + class_exists('Guzzle\Http\Exception\CurlException'); + } + + public function add(RequestInterface $request) + { + $this->queued[] = $request; + + return $this; + } + + public function all() + { + $requests = $this->queued; + foreach ($this->handles as $handle) { + $requests = array_merge($requests, $handle->all()); + } + + return $requests; + } + + public function remove(RequestInterface $request) + { + foreach ($this->queued as $i => $r) { + if ($request === $r) { + unset($this->queued[$i]); + return true; + } + } + + foreach ($this->handles as $handle) { + if ($handle->remove($request)) { + return true; + } + } + + return false; + } + + public function reset($hard = false) + { + $this->queued = array(); + $this->groups = array(); + foreach ($this->handles as $handle) { + $handle->reset(); + } + if ($hard) { + $this->handles = array(); + } + + return $this; + } + + public function send() + { + if ($this->queued) { + $group = $this->getAvailableHandle(); + // Add this handle to a list of handles than is claimed + $this->groups[] = $group; + while ($request = array_shift($this->queued)) { + $group->add($request); + } + try { + $group->send(); + array_pop($this->groups); + $this->cleanupHandles(); + } catch (\Exception $e) { + // Remove the group and cleanup if an exception was encountered and no more requests in group + if (!$group->count()) { + array_pop($this->groups); + $this->cleanupHandles(); + } + throw $e; + } + } + } + + public function count() + { + return count($this->all()); + } + + /** + * Get an existing available CurlMulti handle or create a new one + * + * @return CurlMulti + */ + protected function getAvailableHandle() + { + // Grab a handle that is not claimed + foreach ($this->handles as $h) { + if (!in_array($h, $this->groups, true)) { + return $h; + } + } + + // All are claimed, so create one + $handle = new CurlMulti($this->selectTimeout); + $handle->setEventDispatcher($this->getEventDispatcher()); + $this->handles[] = $handle; + + return $handle; + } + + /** + * Trims down unused CurlMulti handles to limit the number of open connections + */ + protected function cleanupHandles() + { + if ($diff = max(0, count($this->handles) - $this->maxHandles)) { + for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { + if (!count($this->handles[$i])) { + unset($this->handles[$i]); + $diff--; + } + } + $this->handles = array_values($this->handles); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php new file mode 100644 index 000000000..c3f99dd25 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php @@ -0,0 +1,66 @@ +version) { + $this->version = curl_version(); + } + + return $this->version; + } + + /** + * Get a specific type of curl information + * + * @param string $type Version information to retrieve. This value is one of: + * - version_number: cURL 24 bit version number + * - version: cURL version number, as a string + * - ssl_version_number: OpenSSL 24 bit version number + * - ssl_version: OpenSSL version number, as a string + * - libz_version: zlib version number, as a string + * - host: Information about the host where cURL was built + * - features: A bitmask of the CURL_VERSION_XXX constants + * - protocols: An array of protocols names supported by cURL + * + * @return string|float|bool if the $type is found, and false if not found + */ + public function get($type) + { + $version = $this->getAll(); + + return isset($version[$type]) ? $version[$type] : false; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php new file mode 100644 index 000000000..5d1a0cd87 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php @@ -0,0 +1,147 @@ +request = $request; + $this->emitIo = $emitIo; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader($curl, $header) + { + static $normalize = array("\r", "\n"); + $length = strlen($header); + $header = str_replace($normalize, '', $header); + + if (strpos($header, 'HTTP/') === 0) { + + $startLine = explode(' ', $header, 3); + $code = $startLine[1]; + $status = isset($startLine[2]) ? $startLine[2] : ''; + + // Only download the body of the response to the specified response + // body when a successful response is received. + if ($code >= 200 && $code < 300) { + $body = $this->request->getResponseBody(); + } else { + $body = EntityBody::factory(); + } + + $response = new Response($code, null, $body); + $response->setStatus($code, $status); + $this->request->startResponse($response); + + $this->request->dispatch('request.receive.status_line', array( + 'request' => $this, + 'line' => $header, + 'status_code' => $code, + 'reason_phrase' => $status + )); + + } elseif ($pos = strpos($header, ':')) { + $this->request->getResponse()->addHeader( + trim(substr($header, 0, $pos)), + trim(substr($header, $pos + 1)) + ); + } + + return $length; + } + + /** + * Received a progress notification + * + * @param int $downloadSize Total download size + * @param int $downloaded Amount of bytes downloaded + * @param int $uploadSize Total upload size + * @param int $uploaded Amount of bytes uploaded + * @param resource $handle CurlHandle object + */ + public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) + { + $this->request->dispatch('curl.callback.progress', array( + 'request' => $this->request, + 'handle' => $handle, + 'download_size' => $downloadSize, + 'downloaded' => $downloaded, + 'upload_size' => $uploadSize, + 'uploaded' => $uploaded + )); + } + + /** + * Write data to the response body of a request + * + * @param resource $curl Curl handle + * @param string $write Data that was received + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if ($this->emitIo) { + $this->request->dispatch('curl.callback.write', array( + 'request' => $this->request, + 'write' => $write + )); + } + + if ($response = $this->request->getResponse()) { + return $response->getBody()->write($write); + } else { + // Unexpected data received before response headers - abort transfer + return 0; + } + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + if (!($body = $this->request->getBody())) { + return ''; + } + + $read = (string) $body->read($length); + if ($this->emitIo) { + $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); + } + + return $read; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php new file mode 100644 index 000000000..b60d170f0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php @@ -0,0 +1,201 @@ +rewindFunction = $callable; + + return $this; + } + + public function rewind() + { + return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); + } + + /** + * Create a new EntityBody from a string + * + * @param string $string String of data + * + * @return EntityBody + */ + public static function fromString($string) + { + $stream = fopen('php://temp', 'r+'); + if ($string !== '') { + fwrite($stream, $string); + rewind($stream); + } + + return new static($stream); + } + + public function compress($filter = 'zlib.deflate') + { + $result = $this->handleCompression($filter); + $this->contentEncoding = $result ? $filter : false; + + return $result; + } + + public function uncompress($filter = 'zlib.inflate') + { + $offsetStart = 0; + + // When inflating gzipped data, the first 10 bytes must be stripped + // if a gzip header is present + if ($filter == 'zlib.inflate') { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { + $offsetStart = 10; + } + } + + $this->contentEncoding = false; + + return $this->handleCompression($filter, $offsetStart); + } + + public function getContentLength() + { + return $this->getSize(); + } + + public function getContentType() + { + return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; + } + + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + if ($hash = self::getHash($this, 'md5', $rawOutput)) { + return $hash && $base64Encode ? base64_encode($hash) : $hash; + } else { + return false; + } + } + + /** + * Calculate the MD5 hash of an entity body + * + * @param EntityBodyInterface $body Entity body to calculate the hash for + * @param bool $rawOutput Whether or not to use raw output + * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) + * + * @return bool|string Returns an MD5 string on success or FALSE on failure + * @deprecated This will be deprecated soon + * @codeCoverageIgnore + */ + public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) + { + Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); + return $body->getContentMd5($rawOutput, $base64Encode); + } + + public function setStreamFilterContentEncoding($streamFilterContentEncoding) + { + $this->contentEncoding = $streamFilterContentEncoding; + + return $this; + } + + public function getContentEncoding() + { + return strtr($this->contentEncoding, array( + 'zlib.deflate' => 'gzip', + 'bzip2.compress' => 'compress' + )) ?: false; + } + + protected function handleCompression($filter, $offsetStart = 0) + { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + + $handle = fopen('php://temp', 'r+'); + $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); + if (!$filter) { + return false; + } + + // Seek to the offset start if possible + $this->seek($offsetStart); + while ($data = fread($this->stream, 8096)) { + fwrite($handle, $data); + } + + fclose($this->stream); + $this->stream = $handle; + stream_filter_remove($filter); + $stat = fstat($this->stream); + $this->size = $stat['size']; + $this->rebuildCache(); + $this->seek(0); + + // Remove any existing rewind function as the underlying stream has been replaced + $this->rewindFunction = null; + + return true; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php new file mode 100644 index 000000000..e640f5785 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php @@ -0,0 +1,73 @@ +isClientError()) { + $label = 'Client error response'; + $class = __NAMESPACE__ . '\\ClientErrorResponseException'; + } elseif ($response->isServerError()) { + $label = 'Server error response'; + $class = __NAMESPACE__ . '\\ServerErrorResponseException'; + } else { + $label = 'Unsuccessful response'; + $class = __CLASS__; + } + + $message = $label . PHP_EOL . implode(PHP_EOL, array( + '[status code] ' . $response->getStatusCode(), + '[reason phrase] ' . $response->getReasonPhrase(), + '[url] ' . $request->getUrl(), + )); + + $e = new $class($message); + $e->setResponse($response); + $e->setRequest($request); + + return $e; + } + + /** + * Set the response that caused the exception + * + * @param Response $response Response to set + */ + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Get the response that caused the exception + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php new file mode 100644 index 000000000..04d7ddc05 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php @@ -0,0 +1,8 @@ +curlError = $error; + $this->curlErrorNo = $number; + + return $this; + } + + /** + * Set the associated curl handle + * + * @param CurlHandle $handle Curl handle + * + * @return self + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->handle = $handle; + + return $this; + } + + /** + * Get the associated cURL handle + * + * @return CurlHandle|null + */ + public function getCurlHandle() + { + return $this->handle; + } + + /** + * Get the associated cURL error message + * + * @return string|null + */ + public function getError() + { + return $this->curlError; + } + + /** + * Get the associated cURL error number + * + * @return int|null + */ + public function getErrorNo() + { + return $this->curlErrorNo; + } + + /** + * Returns curl information about the transfer + * + * @return array + */ + public function getCurlInfo() + { + return $this->curlInfo; + } + + /** + * Set curl transfer information + * + * @param array $info Array of curl transfer information + * + * @return self + * @link http://php.net/manual/en/function.curl-getinfo.php + */ + public function setCurlInfo(array $info) + { + $this->curlInfo = $info; + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php new file mode 100644 index 000000000..ee87295d3 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php @@ -0,0 +1,10 @@ +successfulRequests, $this->failedRequests); + } + + /** + * Add to the array of successful requests + * + * @param RequestInterface $request Successful request + * + * @return self + */ + public function addSuccessfulRequest(RequestInterface $request) + { + $this->successfulRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests + * + * @param RequestInterface $request Failed request + * + * @return self + */ + public function addFailedRequest(RequestInterface $request) + { + $this->failedRequests[] = $request; + + return $this; + } + + /** + * Add to the array of failed requests and associate with exceptions + * + * @param RequestInterface $request Failed request + * @param \Exception $exception Exception to add and associate with + * + * @return self + */ + public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) + { + $this->add($exception) + ->addFailedRequest($request) + ->exceptionForRequest[spl_object_hash($request)] = $exception; + + return $this; + } + + /** + * Get the Exception that caused the given $request to fail + * + * @param RequestInterface $request Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedRequest(RequestInterface $request) + { + $oid = spl_object_hash($request); + + return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; + } + + /** + * Set all of the successful requests + * + * @param array Array of requests + * + * @return self + */ + public function setSuccessfulRequests(array $requests) + { + $this->successfulRequests = $requests; + + return $this; + } + + /** + * Set all of the failed requests + * + * @param array Array of requests + * + * @return self + */ + public function setFailedRequests(array $requests) + { + $this->failedRequests = $requests; + + return $this; + } + + /** + * Get an array of successful requests sent in the multi transfer + * + * @return array + */ + public function getSuccessfulRequests() + { + return $this->successfulRequests; + } + + /** + * Get an array of failed requests sent in the multi transfer + * + * @return array + */ + public function getFailedRequests() + { + return $this->failedRequests; + } + + /** + * Check if the exception object contains a request + * + * @param RequestInterface $request Request to check + * + * @return bool + */ + public function containsRequest(RequestInterface $request) + { + return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php new file mode 100644 index 000000000..274df2cb1 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php @@ -0,0 +1,39 @@ +request = $request; + + return $this; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php new file mode 100644 index 000000000..f0f7cfe48 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php @@ -0,0 +1,8 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + public function read($length) + { + $event = array( + 'body' => $this, + 'length' => $length, + 'read' => $this->body->read($length) + ); + $this->dispatch('body.read', $event); + + return $event['read']; + } + + public function write($string) + { + $event = array( + 'body' => $this, + 'write' => $string, + 'result' => $this->body->write($string) + ); + $this->dispatch('body.write', $event); + + return $event['result']; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php new file mode 100644 index 000000000..0d066ffce --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php @@ -0,0 +1,220 @@ +params = new Collection(); + $this->headerFactory = new HeaderFactory(); + $this->headers = new HeaderCollection(); + } + + /** + * Set the header factory to use to create headers + * + * @param HeaderFactoryInterface $factory + * + * @return self + */ + public function setHeaderFactory(HeaderFactoryInterface $factory) + { + $this->headerFactory = $factory; + + return $this; + } + + public function getParams() + { + return $this->params; + } + + public function addHeader($header, $value) + { + if (isset($this->headers[$header])) { + $this->headers[$header]->add($value); + } elseif ($value instanceof HeaderInterface) { + $this->headers[$header] = $value; + } else { + $this->headers[$header] = $this->headerFactory->createHeader($header, $value); + } + + return $this; + } + + public function addHeaders(array $headers) + { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function getHeader($header) + { + return $this->headers[$header]; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getHeaderLines() + { + $headers = array(); + foreach ($this->headers as $value) { + $headers[] = $value->getName() . ': ' . $value; + } + + return $headers; + } + + public function setHeader($header, $value) + { + unset($this->headers[$header]); + $this->addHeader($header, $value); + + return $this; + } + + public function setHeaders(array $headers) + { + $this->headers->clear(); + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + public function hasHeader($header) + { + return isset($this->headers[$header]); + } + + public function removeHeader($header) + { + unset($this->headers[$header]); + + return $this; + } + + /** + * @deprecated Use $message->getHeader()->parseParams() + * @codeCoverageIgnore + */ + public function getTokenizedHeader($header, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); + if ($this->hasHeader($header)) { + $data = new Collection(); + foreach ($this->getHeader($header)->parseParams() as $values) { + foreach ($values as $key => $value) { + if ($value === '') { + $data->set($data->count(), $key); + } else { + $data->add($key, $value); + } + } + } + return $data; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setTokenizedHeader($header, $data, $token = ';') + { + Version::warn(__METHOD__ . ' is deprecated.'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + return null; + } + + return $header->getDirective($directive); + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + return $header->hasDirective($directive); + } else { + return false; + } + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function addCacheControlDirective($directive, $value = true) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); + if (!($header = $this->getHeader('Cache-Control'))) { + $this->addHeader('Cache-Control', ''); + $header = $this->getHeader('Cache-Control'); + } + + $header->addDirective($directive, $value); + + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function removeCacheControlDirective($directive) + { + Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); + if ($header = $this->getHeader('Cache-Control')) { + $header->removeDirective($directive); + } + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php new file mode 100644 index 000000000..212850a25 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php @@ -0,0 +1,247 @@ +postFields = new QueryString(); + parent::__construct($method, $url, $headers); + } + + /** + * @return string + */ + public function __toString() + { + // Only attempt to include the POST data if it's only fields + if (count($this->postFields) && empty($this->postFiles)) { + return parent::__toString() . (string) $this->postFields; + } + + return parent::__toString() . $this->body; + } + + public function setState($state, array $context = array()) + { + parent::setState($state, $context); + if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { + $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); + } + + return $this->state; + } + + public function setBody($body, $contentType = null) + { + $this->body = EntityBody::factory($body); + + // Auto detect the Content-Type from the path of the request if possible + if ($contentType === null && !$this->hasHeader('Content-Type')) { + $contentType = $this->body->getContentType(); + } + + if ($contentType) { + $this->setHeader('Content-Type', $contentType); + } + + // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. + if (!$this->body->isSeekable() && $this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + + // Set the Content-Length header if it can be determined + $size = $this->body->getContentLength(); + if ($size !== null && $size !== false) { + $this->setHeader('Content-Length', $size); + if ($size > $this->expectCutoff) { + $this->setHeader('Expect', '100-Continue'); + } + } elseif (!$this->hasHeader('Content-Length')) { + if ('1.1' == $this->protocolVersion) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + throw new RequestException( + 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' + ); + } + } + + return $this; + } + + public function getBody() + { + return $this->body; + } + + /** + * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. + * + * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) + * + * @return self + */ + public function setExpectHeaderCutoff($size) + { + $this->expectCutoff = $size; + if ($size === false || !$this->body) { + $this->removeHeader('Expect'); + } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { + $this->setHeader('Expect', '100-Continue'); + } + + return $this; + } + + public function configureRedirects($strict = false, $maxRedirects = 5) + { + $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); + if ($maxRedirects == 0) { + $this->getParams()->set(RedirectPlugin::DISABLE, true); + } else { + $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); + } + + return $this; + } + + public function getPostField($field) + { + return $this->postFields->get($field); + } + + public function getPostFields() + { + return $this->postFields; + } + + public function setPostField($key, $value) + { + $this->postFields->set($key, $value); + $this->processPostFields(); + + return $this; + } + + public function addPostFields($fields) + { + $this->postFields->merge($fields); + $this->processPostFields(); + + return $this; + } + + public function removePostField($field) + { + $this->postFields->remove($field); + $this->processPostFields(); + + return $this; + } + + public function getPostFiles() + { + return $this->postFiles; + } + + public function getPostFile($fieldName) + { + return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; + } + + public function removePostFile($fieldName) + { + unset($this->postFiles[$fieldName]); + $this->processPostFields(); + + return $this; + } + + public function addPostFile($field, $filename = null, $contentType = null, $postname = null) + { + $data = null; + + if ($field instanceof PostFileInterface) { + $data = $field; + } elseif (is_array($filename)) { + // Allow multiple values to be set in a single key + foreach ($filename as $file) { + $this->addPostFile($field, $file, $contentType); + } + return $this; + } elseif (!is_string($filename)) { + throw new RequestException('The path to a file must be a string'); + } elseif (!empty($filename)) { + // Adding an empty file will cause cURL to error out + $data = new PostFile($field, $filename, $contentType, $postname); + } + + if ($data) { + if (!isset($this->postFiles[$data->getFieldName()])) { + $this->postFiles[$data->getFieldName()] = array($data); + } else { + $this->postFiles[$data->getFieldName()][] = $data; + } + $this->processPostFields(); + } + + return $this; + } + + public function addPostFiles(array $files) + { + foreach ($files as $key => $file) { + if ($file instanceof PostFileInterface) { + $this->addPostFile($file, null, null, false); + } elseif (is_string($file)) { + // Convert non-associative array keys into 'file' + if (is_numeric($key)) { + $key = 'file'; + } + $this->addPostFile($key, $file, null, false); + } else { + throw new RequestException('File must be a string or instance of PostFileInterface'); + } + } + + return $this; + } + + /** + * Determine what type of request should be sent based on post fields + */ + protected function processPostFields() + { + if (!$this->postFiles) { + $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); + } else { + $this->setHeader('Content-Type', self::MULTIPART); + if ($this->expectCutoff !== false) { + $this->setHeader('Expect', '100-Continue'); + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php new file mode 100644 index 000000000..49ad4595d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php @@ -0,0 +1,137 @@ + filenames where filename can be a string or PostFileInterface + * + * @return self + */ + public function addPostFiles(array $files); + + /** + * Configure how redirects are handled for the request + * + * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most + * browsers with follow a 301-302 redirect for a POST request with a GET request. This is + * the default behavior of Guzzle. Enable strict redirects to redirect these responses + * with a POST rather than a GET request. + * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. + * + * @return self + */ + public function configureRedirects($strict = false, $maxRedirects = 5); +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php new file mode 100644 index 000000000..50597b2a6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php @@ -0,0 +1,182 @@ +header = trim($header); + $this->glue = $glue; + + foreach ((array) $values as $value) { + foreach ((array) $value as $v) { + $this->values[] = $v; + } + } + } + + public function __toString() + { + return implode($this->glue . ' ', $this->toArray()); + } + + public function add($value) + { + $this->values[] = $value; + + return $this; + } + + public function getName() + { + return $this->header; + } + + public function setName($name) + { + $this->header = $name; + + return $this; + } + + public function setGlue($glue) + { + $this->glue = $glue; + + return $this; + } + + public function getGlue() + { + return $this->glue; + } + + /** + * Normalize the header to be a single header with an array of values. + * + * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into + * multiple entries in the header. + * + * @return self + */ + public function normalize() + { + $values = $this->toArray(); + + for ($i = 0, $total = count($values); $i < $total; $i++) { + if (strpos($values[$i], $this->glue) !== false) { + // Explode on glue when the glue is not inside of a comma + foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { + $values[] = trim($v); + } + unset($values[$i]); + } + } + + $this->values = array_values($values); + + return $this; + } + + public function hasValue($searchValue) + { + return in_array($searchValue, $this->toArray()); + } + + public function removeValue($searchValue) + { + $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { + return $value != $searchValue; + })); + + return $this; + } + + public function toArray() + { + return $this->values; + } + + public function count() + { + return count($this->toArray()); + } + + public function getIterator() + { + return new \ArrayIterator($this->toArray()); + } + + public function parseParams() + { + $params = $matches = array(); + $callback = array($this, 'trimHeader'); + + // Normalize the header into a single array and iterate over all values + foreach ($this->normalize()->toArray() as $val) { + $part = array(); + foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { + if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { + continue; + } + $pieces = array_map($callback, $matches[0]); + $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; + } + if ($part) { + $params[] = $part; + } + } + + return $params; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function hasExactHeader($header) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this->header == $header; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function raw() + { + Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); + return $this->toArray(); + } + + /** + * Trim a header by removing excess spaces and wrapping quotes + * + * @param $str + * + * @return string + */ + protected function trimHeader($str) + { + static $trimmed = "\"' \n\t"; + + return trim($str, $trimmed); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php new file mode 100644 index 000000000..77789e51f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php @@ -0,0 +1,121 @@ +directives = null; + } + + public function removeValue($searchValue) + { + parent::removeValue($searchValue); + $this->directives = null; + } + + /** + * Check if a specific cache control directive exists + * + * @param string $param Directive to retrieve + * + * @return bool + */ + public function hasDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]); + } + + /** + * Get a specific cache control directive + * + * @param string $param Directive to retrieve + * + * @return string|bool|null + */ + public function getDirective($param) + { + $directives = $this->getDirectives(); + + return isset($directives[$param]) ? $directives[$param] : null; + } + + /** + * Add a cache control directive + * + * @param string $param Directive to add + * @param string $value Value to set + * + * @return self + */ + public function addDirective($param, $value) + { + $directives = $this->getDirectives(); + $directives[$param] = $value; + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Remove a cache control directive by name + * + * @param string $param Directive to remove + * + * @return self + */ + public function removeDirective($param) + { + $directives = $this->getDirectives(); + unset($directives[$param]); + $this->updateFromDirectives($directives); + + return $this; + } + + /** + * Get an associative array of cache control directives + * + * @return array + */ + public function getDirectives() + { + if ($this->directives === null) { + $this->directives = array(); + foreach ($this->parseParams() as $collection) { + foreach ($collection as $key => $value) { + $this->directives[$key] = $value === '' ? true : $value; + } + } + } + + return $this->directives; + } + + /** + * Updates the header value based on the parsed directives + * + * @param array $directives Array of cache control directives + */ + protected function updateFromDirectives(array $directives) + { + $this->directives = $directives; + $this->values = array(); + + foreach ($directives as $key => $value) { + $this->values[] = $value === true ? $key : "{$key}={$value}"; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php new file mode 100644 index 000000000..8c7f6aefb --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php @@ -0,0 +1,108 @@ +headers = $headers; + } + + public function __clone() + { + foreach ($this->headers as &$header) { + $header = clone $header; + } + } + + /** + * Clears the header collection + */ + public function clear() + { + $this->headers = array(); + } + + /** + * Set a header on the collection + * + * @param HeaderInterface $header Header to add + * + * @return self + */ + public function add(HeaderInterface $header) + { + $this->headers[strtolower($header->getName())] = $header; + + return $this; + } + + /** + * Get an array of header objects + * + * @return array + */ + public function getAll() + { + return $this->headers; + } + + /** + * Alias of offsetGet + */ + public function get($key) + { + return $this->offsetGet($key); + } + + public function count() + { + return count($this->headers); + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetGet($offset) + { + $l = strtolower($offset); + + return isset($this->headers[$l]) ? $this->headers[$l] : null; + } + + public function offsetSet($offset, $value) + { + $this->add($value); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + public function getIterator() + { + return new \ArrayIterator($this->headers); + } + + public function toArray() + { + $result = array(); + foreach ($this->headers as $header) { + $result[$header->getName()] = $header->toArray(); + } + + return $result; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php new file mode 100644 index 000000000..0273be52f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php @@ -0,0 +1,26 @@ + 'Guzzle\Http\Message\Header\CacheControl', + 'link' => 'Guzzle\Http\Message\Header\Link', + ); + + public function createHeader($header, $value = null) + { + $lowercase = strtolower($header); + + return isset($this->mapping[$lowercase]) + ? new $this->mapping[$lowercase]($header, $value) + : new Header($header, $value); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php new file mode 100644 index 000000000..9457cf64a --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php @@ -0,0 +1,19 @@ +", "rel=\"{$rel}\""); + + foreach ($params as $k => $v) { + $values[] = "{$k}=\"{$v}\""; + } + + return $this->add(implode('; ', $values)); + } + + /** + * Check if a specific link exists for a given rel attribute + * + * @param string $rel rel value + * + * @return bool + */ + public function hasLink($rel) + { + return $this->getLink($rel) !== null; + } + + /** + * Get a specific link for a given rel attribute + * + * @param string $rel Rel value + * + * @return array|null + */ + public function getLink($rel) + { + foreach ($this->getLinks() as $link) { + if (isset($link['rel']) && $link['rel'] == $rel) { + return $link; + } + } + + return null; + } + + /** + * Get an associative array of links + * + * For example: + * Link: ; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg" + * + * + * var_export($response->getLinks()); + * array( + * array( + * 'url' => 'http:/.../front.jpeg', + * 'rel' => 'back', + * 'type' => 'image/jpeg', + * ) + * ) + * + * + * @return array + */ + public function getLinks() + { + $links = $this->parseParams(); + + foreach ($links as &$link) { + $key = key($link); + unset($link[$key]); + $link['url'] = trim($key, '<> '); + } + + return $links; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php new file mode 100644 index 000000000..62bcd4391 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php @@ -0,0 +1,102 @@ +fieldName = $fieldName; + $this->setFilename($filename); + $this->postname = $postname ? $postname : basename($filename); + $this->contentType = $contentType ?: $this->guessContentType(); + } + + public function setFieldName($name) + { + $this->fieldName = $name; + + return $this; + } + + public function getFieldName() + { + return $this->fieldName; + } + + public function setFilename($filename) + { + // Remove leading @ symbol + if (strpos($filename, '@') === 0) { + $filename = substr($filename, 1); + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $this->filename = $filename; + + return $this; + } + + public function setPostname($postname) + { + $this->postname = $postname; + + return $this; + } + + public function getFilename() + { + return $this->filename; + } + + public function getPostname() + { + return $this->postname; + } + + public function setContentType($type) + { + $this->contentType = $type; + + return $this; + } + + public function getContentType() + { + return $this->contentType; + } + + public function getCurlValue() + { + // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax + // See: https://wiki.php.net/rfc/curl-file-upload + if (function_exists('curl_file_create')) { + return curl_file_create($this->filename, $this->contentType, $this->postname); + } + + // Use the old style if using an older version of PHP + $value = "@{$this->filename};filename=" . $this->postname; + if ($this->contentType) { + $value .= ';type=' . $this->contentType; + } + + return $value; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getCurlString() + { + Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); + return $this->getCurlValue(); + } + + /** + * Determine the Content-Type of the file + */ + protected function guessContentType() + { + return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php new file mode 100644 index 000000000..7f0779d1e --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php @@ -0,0 +1,83 @@ +method = strtoupper($method); + $this->curlOptions = new Collection(); + $this->setUrl($url); + + if ($headers) { + // Special handling for multi-value headers + foreach ($headers as $key => $value) { + // Deal with collisions with Host and Authorization + if ($key == 'host' || $key == 'Host') { + $this->setHeader($key, $value); + } elseif ($value instanceof HeaderInterface) { + $this->addHeader($key, $value); + } else { + foreach ((array) $value as $v) { + $this->addHeader($key, $v); + } + } + } + } + + $this->setState(self::STATE_NEW); + } + + public function __clone() + { + if ($this->eventDispatcher) { + $this->eventDispatcher = clone $this->eventDispatcher; + } + $this->curlOptions = clone $this->curlOptions; + $this->params = clone $this->params; + $this->url = clone $this->url; + $this->response = $this->responseBody = null; + $this->headers = clone $this->headers; + + $this->setState(RequestInterface::STATE_NEW); + $this->dispatch('request.clone', array('request' => $this)); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + return $this->getRawHeaders() . "\r\n\r\n"; + } + + /** + * Default method that will throw exceptions if an unsuccessful response is received. + * + * @param Event $event Received + * @throws BadResponseException if the response is not successful + */ + public static function onRequestError(Event $event) + { + $e = BadResponseException::factory($event['request'], $event['response']); + $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); + throw $e; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getClient() + { + return $this->client; + } + + public function getRawHeaders() + { + $protocolVersion = $this->protocolVersion ?: '1.1'; + + return trim($this->method . ' ' . $this->getResource()) . ' ' + . strtoupper(str_replace('https', 'http', $this->url->getScheme())) + . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); + } + + public function setUrl($url) + { + if ($url instanceof Url) { + $this->url = $url; + } else { + $this->url = Url::factory($url); + } + + // Update the port and host header + $this->setPort($this->url->getPort()); + + if ($this->url->getUsername() || $this->url->getPassword()) { + $this->setAuth($this->url->getUsername(), $this->url->getPassword()); + // Remove the auth info from the URL + $this->url->setUsername(null); + $this->url->setPassword(null); + } + + return $this; + } + + public function send() + { + if (!$this->client) { + throw new RuntimeException('A client must be set on the request'); + } + + return $this->client->send($this); + } + + public function getResponse() + { + return $this->response; + } + + public function getQuery($asString = false) + { + return $asString + ? (string) $this->url->getQuery() + : $this->url->getQuery(); + } + + public function getMethod() + { + return $this->method; + } + + public function getScheme() + { + return $this->url->getScheme(); + } + + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + public function getHost() + { + return $this->url->getHost(); + } + + public function setHost($host) + { + $this->url->setHost($host); + $this->setPort($this->url->getPort()); + + return $this; + } + + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + public function setProtocolVersion($protocol) + { + $this->protocolVersion = $protocol; + + return $this; + } + + public function getPath() + { + return '/' . ltrim($this->url->getPath(), '/'); + } + + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + public function getPort() + { + return $this->url->getPort(); + } + + public function setPort($port) + { + $this->url->setPort($port); + + // Include the port in the Host header if it is not the default port for the scheme of the URL + $scheme = $this->url->getScheme(); + if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); + } else { + $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); + } + + return $this; + } + + public function getUsername() + { + return $this->username; + } + + public function getPassword() + { + return $this->password; + } + + public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) + { + static $authMap = array( + 'basic' => CURLAUTH_BASIC, + 'digest' => CURLAUTH_DIGEST, + 'ntlm' => CURLAUTH_NTLM, + 'any' => CURLAUTH_ANY + ); + + // If we got false or null, disable authentication + if (!$user) { + $this->password = $this->username = null; + $this->removeHeader('Authorization'); + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + return $this; + } + + if (!is_numeric($scheme)) { + $scheme = strtolower($scheme); + if (!isset($authMap[$scheme])) { + throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); + } + $scheme = $authMap[$scheme]; + } + + $this->username = $user; + $this->password = $password; + + // Bypass CURL when using basic auth to promote connection reuse + if ($scheme == CURLAUTH_BASIC) { + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); + } else { + $this->getCurlOptions() + ->set(CURLOPT_HTTPAUTH, $scheme) + ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + + return $this; + } + + public function getResource() + { + $resource = $this->getPath(); + if ($query = (string) $this->url->getQuery()) { + $resource .= '?' . $query; + } + + return $resource; + } + + public function getUrl($asObject = false) + { + return $asObject ? clone $this->url : (string) $this->url; + } + + public function getState() + { + return $this->state; + } + + public function setState($state, array $context = array()) + { + $oldState = $this->state; + $this->state = $state; + + switch ($state) { + case self::STATE_NEW: + $this->response = null; + break; + case self::STATE_TRANSFER: + if ($oldState !== $state) { + // Fix Content-Length and Transfer-Encoding collisions + if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { + $this->removeHeader('Transfer-Encoding'); + } + $this->dispatch('request.before_send', array('request' => $this)); + } + break; + case self::STATE_COMPLETE: + if ($oldState !== $state) { + $this->processResponse($context); + $this->responseBody = null; + } + break; + case self::STATE_ERROR: + if (isset($context['exception'])) { + $this->dispatch('request.exception', array( + 'request' => $this, + 'response' => isset($context['response']) ? $context['response'] : $this->response, + 'exception' => isset($context['exception']) ? $context['exception'] : null + )); + } + } + + return $this->state; + } + + public function getCurlOptions() + { + return $this->curlOptions; + } + + public function startResponse(Response $response) + { + $this->state = self::STATE_TRANSFER; + $response->setEffectiveUrl((string) $this->getUrl()); + $this->response = $response; + + return $this; + } + + public function setResponse(Response $response, $queued = false) + { + $response->setEffectiveUrl((string) $this->url); + + if ($queued) { + $ed = $this->getEventDispatcher(); + $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { + $e['request']->setResponse($response); + $ed->removeListener('request.before_send', $f); + }, -9999); + } else { + $this->response = $response; + // If a specific response body is specified, then use it instead of the response's body + if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { + $this->getResponseBody()->write((string) $this->response->getBody()); + } else { + $this->responseBody = $this->response->getBody(); + } + $this->setState(self::STATE_COMPLETE); + } + + return $this; + } + + public function setResponseBody($body) + { + // Attempt to open a file for writing if a string was passed + if (is_string($body)) { + // @codeCoverageIgnoreStart + if (!($body = fopen($body, 'w+'))) { + throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); + } + // @codeCoverageIgnoreEnd + } + + $this->responseBody = EntityBody::factory($body); + + return $this; + } + + public function getResponseBody() + { + if ($this->responseBody === null) { + $this->responseBody = EntityBody::factory()->setCustomData('default', true); + } + + return $this->responseBody; + } + + /** + * Determine if the response body is repeatable (readable + seekable) + * + * @return bool + * @deprecated Use getResponseBody()->isSeekable() + * @codeCoverageIgnore + */ + public function isResponseBodyRepeatable() + { + Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); + return !$this->responseBody ? true : $this->responseBody->isRepeatable(); + } + + public function getCookies() + { + if ($cookie = $this->getHeader('Cookie')) { + $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); + return $data['cookies']; + } + + return array(); + } + + public function getCookie($name) + { + $cookies = $this->getCookies(); + + return isset($cookies[$name]) ? $cookies[$name] : null; + } + + public function addCookie($name, $value) + { + if (!$this->hasHeader('Cookie')) { + $this->setHeader('Cookie', "{$name}={$value}"); + } else { + $this->getHeader('Cookie')->add("{$name}={$value}"); + } + + // Always use semicolons to separate multiple cookie headers + $this->getHeader('Cookie')->setGlue(';'); + + return $this; + } + + public function removeCookie($name) + { + if ($cookie = $this->getHeader('Cookie')) { + foreach ($cookie as $cookieValue) { + if (strpos($cookieValue, $name . '=') === 0) { + $cookie->removeValue($cookieValue); + } + } + } + + return $this; + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); + + return $this; + } + + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->setEventDispatcher(new EventDispatcher()); + } + + return $this->eventDispatcher; + } + + public function dispatch($eventName, array $context = array()) + { + $context['request'] = $this; + + return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + /** + * Get an array containing the request and response for event notifications + * + * @return array + */ + protected function getEventArray() + { + return array( + 'request' => $this, + 'response' => $this->response + ); + } + + /** + * Process a received response + * + * @param array $context Contextual information + * @throws RequestException|BadResponseException on unsuccessful responses + */ + protected function processResponse(array $context = array()) + { + if (!$this->response) { + // If no response, then processResponse shouldn't have been called + $e = new RequestException('Error completing request'); + $e->setRequest($this); + throw $e; + } + + $this->state = self::STATE_COMPLETE; + + // A request was sent, but we don't know if we'll send more or if the final response will be successful + $this->dispatch('request.sent', $this->getEventArray() + $context); + + // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) + if ($this->state == RequestInterface::STATE_COMPLETE) { + + // The request completed, so the HTTP transaction is complete + $this->dispatch('request.complete', $this->getEventArray()); + + // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by + // modifying the Event object in your listeners or calling setResponse() on the request + if ($this->response->isError()) { + $event = new Event($this->getEventArray()); + $this->getEventDispatcher()->dispatch('request.error', $event); + // Allow events of request.error to quietly change the response + if ($event['response'] !== $this->response) { + $this->response = $event['response']; + } + } + + // If a successful response was received, dispatch an event + if ($this->response->isSuccessful()) { + $this->dispatch('request.success', $this->getEventArray()); + } + } + } + + /** + * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy + * @codeCoverageIgnore + */ + public function canCache() + { + Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); + if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { + $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); + return $canCache->canCacheRequest($this); + } else { + return false; + } + } + + /** + * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) + * @codeCoverageIgnore + */ + public function setIsRedirect($isRedirect) + { + $this->isRedirect = $isRedirect; + + return $this; + } + + /** + * @deprecated Use the history plugin + * @codeCoverageIgnore + */ + public function isRedirect() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); + return $this->isRedirect; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php new file mode 100644 index 000000000..598a2f697 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php @@ -0,0 +1,359 @@ +methods = array_flip(get_class_methods(__CLASS__)); + } + + public function fromMessage($message) + { + $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); + + if (!$parsed) { + return false; + } + + $request = $this->fromParts($parsed['method'], $parsed['request_url'], + $parsed['headers'], $parsed['body'], $parsed['protocol'], + $parsed['version']); + + // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST + // requests. This factory method should accurately reflect the message, so here we are removing the Expect + // header if one was not supplied in the message. + if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { + $request->removeHeader('Expect'); + } + + return $request; + } + + public function fromParts( + $method, + array $urlParts, + $headers = null, + $body = null, + $protocol = 'HTTP', + $protocolVersion = '1.1' + ) { + return $this->create($method, Url::buildUrl($urlParts), $headers, $body) + ->setProtocolVersion($protocolVersion); + } + + public function create($method, $url, $headers = null, $body = null, array $options = array()) + { + $method = strtoupper($method); + + if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') { + // Handle non-entity-enclosing request methods + $request = new $this->requestClass($method, $url, $headers); + if ($body) { + // The body is where the response body will be stored + $type = gettype($body); + if ($type == 'string' || $type == 'resource' || $type == 'object') { + $request->setResponseBody($body); + } + } + } else { + // Create an entity enclosing request by default + $request = new $this->entityEnclosingRequestClass($method, $url, $headers); + if ($body || $body === '0') { + // Add POST fields and files to an entity enclosing request if an array is used + if (is_array($body) || $body instanceof Collection) { + // Normalize PHP style cURL uploads with a leading '@' symbol + foreach ($body as $key => $value) { + if (is_string($value) && substr($value, 0, 1) == '@') { + $request->addPostFile($key, $value); + unset($body[$key]); + } + } + // Add the fields if they are still present and not all files + $request->addPostFields($body); + } else { + // Add a raw entity body body to the request + $request->setBody($body, (string) $request->getHeader('Content-Type')); + if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { + $request->removeHeader('Content-Length'); + } + } + } + } + + if ($options) { + $this->applyOptions($request, $options); + } + + return $request; + } + + /** + * Clone a request while changing the method. Emulates the behavior of + * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. + * + * @param RequestInterface $request Request to clone + * @param string $method Method to set + * + * @return RequestInterface + */ + public function cloneRequestWithMethod(RequestInterface $request, $method) + { + // Create the request with the same client if possible + if ($request->getClient()) { + $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); + } else { + $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); + } + + $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); + $cloned->setEventDispatcher(clone $request->getEventDispatcher()); + // Ensure that that the Content-Length header is not copied if changing to GET or HEAD + if (!($cloned instanceof EntityEnclosingRequestInterface)) { + $cloned->removeHeader('Content-Length'); + } elseif ($request instanceof EntityEnclosingRequestInterface) { + $cloned->setBody($request->getBody()); + } + $cloned->getParams()->replace($request->getParams()->toArray()); + $cloned->dispatch('request.clone', array('request' => $cloned)); + + return $cloned; + } + + public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) + { + // Iterate over each key value pair and attempt to apply a config using function visitors + foreach ($options as $key => $value) { + $method = "visit_{$key}"; + if (isset($this->methods[$method])) { + $this->{$method}($request, $value, $flags); + } + } + } + + protected function visit_headers(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('headers value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge headers in but do not overwrite existing values + foreach ($value as $key => $header) { + if (!$request->hasHeader($key)) { + $request->setHeader($key, $header); + } + } + } else { + $request->addHeaders($value); + } + } + + protected function visit_body(RequestInterface $request, $value, $flags) + { + if ($request instanceof EntityEnclosingRequestInterface) { + $request->setBody($value); + } else { + throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); + } + } + + protected function visit_allow_redirects(RequestInterface $request, $value, $flags) + { + if ($value === false) { + $request->getParams()->set(RedirectPlugin::DISABLE, true); + } + } + + protected function visit_auth(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('auth value must be an array'); + } + + $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); + } + + protected function visit_query(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('query value must be an array'); + } + + if ($flags & self::OPTIONS_AS_DEFAULTS) { + // Merge query string values in but do not overwrite existing values + $query = $request->getQuery(); + $query->overwriteWith(array_diff_key($value, $query->toArray())); + } else { + $request->getQuery()->overwriteWith($value); + } + } + + protected function visit_cookies(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('cookies value must be an array'); + } + + foreach ($value as $name => $v) { + $request->addCookie($name, $v); + } + } + + protected function visit_events(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('events value must be an array'); + } + + foreach ($value as $name => $method) { + if (is_array($method)) { + $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); + } else { + $request->getEventDispatcher()->addListener($name, $method); + } + } + } + + protected function visit_plugins(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('plugins value must be an array'); + } + + foreach ($value as $plugin) { + $request->addSubscriber($plugin); + } + } + + protected function visit_exceptions(RequestInterface $request, $value, $flags) + { + if ($value === false || $value === 0) { + $dispatcher = $request->getEventDispatcher(); + foreach ($dispatcher->getListeners('request.error') as $listener) { + if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { + $dispatcher->removeListener('request.error', $listener); + break; + } + } + } + } + + protected function visit_save_to(RequestInterface $request, $value, $flags) + { + $request->setResponseBody($value); + } + + protected function visit_params(RequestInterface $request, $value, $flags) + { + if (!is_array($value)) { + throw new InvalidArgumentException('params value must be an array'); + } + + $request->getParams()->overwriteWith($value); + } + + protected function visit_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_TIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value); + } + } + + protected function visit_connect_timeout(RequestInterface $request, $value, $flags) + { + if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); + } else { + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value); + } + } + + protected function visit_debug(RequestInterface $request, $value, $flags) + { + if ($value) { + $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); + } + } + + protected function visit_verify(RequestInterface $request, $value, $flags) + { + $curl = $request->getCurlOptions(); + if ($value === true || is_string($value)) { + $curl[CURLOPT_SSL_VERIFYHOST] = 2; + $curl[CURLOPT_SSL_VERIFYPEER] = true; + if ($value !== true) { + $curl[CURLOPT_CAINFO] = $value; + } + } elseif ($value === false) { + unset($curl[CURLOPT_CAINFO]); + $curl[CURLOPT_SSL_VERIFYHOST] = 0; + $curl[CURLOPT_SSL_VERIFYPEER] = false; + } + } + + protected function visit_proxy(RequestInterface $request, $value, $flags) + { + $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); + } + + protected function visit_cert(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); + } + } + + protected function visit_ssl_key(RequestInterface $request, $value, $flags) + { + if (is_array($value)) { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); + $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); + } else { + $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php new file mode 100644 index 000000000..6088f10e9 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php @@ -0,0 +1,105 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Reserved for WebDAV advanced collections expired proposal', + 426 => 'Upgrade required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 510 => 'Not Extended', + 511 => 'Network Authentication Required', + ); + + /** @var EntityBodyInterface The response body */ + protected $body; + + /** @var string The reason phrase of the response (human readable code) */ + protected $reasonPhrase; + + /** @var string The status code of the response */ + protected $statusCode; + + /** @var array Information about the request */ + protected $info = array(); + + /** @var string The effective URL that returned this response */ + protected $effectiveUrl; + + /** @var array Cacheable response codes (see RFC 2616:13.4) */ + protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); + + /** + * Create a new Response based on a raw response message + * + * @param string $message Response message + * + * @return self|bool Returns false on error + */ + public static function fromMessage($message) + { + $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); + if (!$data) { + return false; + } + + $response = new static($data['code'], $data['headers'], $data['body']); + $response->setProtocol($data['protocol'], $data['version']) + ->setStatus($data['code'], $data['reason_phrase']); + + // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) + $contentLength = (string) $response->getHeader('Content-Length'); + $actualLength = strlen($data['body']); + if (strlen($data['body']) > 0 && $contentLength != $actualLength) { + $response->setHeader('Content-Length', $actualLength); + } + + return $response; + } + + /** + * Construct the response + * + * @param string $statusCode The response status code (e.g. 200, 404, etc) + * @param ToArrayInterface|array $headers The response headers + * @param string|resource|EntityBodyInterface $body The body of the response + * + * @throws BadResponseException if an invalid response code is given + */ + public function __construct($statusCode, $headers = null, $body = null) + { + parent::__construct(); + $this->setStatus($statusCode); + $this->body = EntityBody::factory($body !== null ? $body : ''); + + if ($headers) { + if (is_array($headers)) { + $this->setHeaders($headers); + } elseif ($headers instanceof ToArrayInterface) { + $this->setHeaders($headers->toArray()); + } else { + throw new BadResponseException('Invalid headers argument received'); + } + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + public function serialize() + { + return json_encode(array( + 'status' => $this->statusCode, + 'body' => (string) $this->body, + 'headers' => $this->headers->toArray() + )); + } + + public function unserialize($serialize) + { + $data = json_decode($serialize, true); + $this->__construct($data['status'], $data['headers'], $data['body']); + } + + /** + * Get the response entity body + * + * @param bool $asString Set to TRUE to return a string of the body rather than a full body object + * + * @return EntityBodyInterface|string + */ + public function getBody($asString = false) + { + return $asString ? (string) $this->body : $this->body; + } + + /** + * Set the response entity body + * + * @param EntityBodyInterface|string $body Body to set + * + * @return self + */ + public function setBody($body) + { + $this->body = EntityBody::factory($body); + + return $this; + } + + /** + * Set the protocol and protocol version of the response + * + * @param string $protocol Response protocol + * @param string $version Protocol version + * + * @return self + */ + public function setProtocol($protocol, $version) + { + $this->protocol = $protocol; + $this->protocolVersion = $version; + + return $this; + } + + /** + * Get the protocol used for the response (e.g. HTTP) + * + * @return string + */ + public function getProtocol() + { + return $this->protocol; + } + + /** + * Get the HTTP protocol version + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * Get a cURL transfer information + * + * @param string $key A single statistic to check + * + * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key + * is set and not found + * @link http://www.php.net/manual/en/function.curl-getinfo.php + */ + public function getInfo($key = null) + { + if ($key === null) { + return $this->info; + } elseif (array_key_exists($key, $this->info)) { + return $this->info[$key]; + } else { + return null; + } + } + + /** + * Set the transfer information + * + * @param array $info Array of cURL transfer stats + * + * @return self + */ + public function setInfo(array $info) + { + $this->info = $info; + + return $this; + } + + /** + * Set the response status + * + * @param int $statusCode Response status code to set + * @param string $reasonPhrase Response reason phrase + * + * @return self + * @throws BadResponseException when an invalid response code is received + */ + public function setStatus($statusCode, $reasonPhrase = '') + { + $this->statusCode = (int) $statusCode; + + if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } else { + $this->reasonPhrase = $reasonPhrase; + } + + return $this; + } + + /** + * Get the response status code + * + * @return integer + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Get the entire response as a string + * + * @return string + */ + public function getMessage() + { + $message = $this->getRawHeaders(); + + // Only include the body in the message if the size is < 2MB + $size = $this->body->getSize(); + if ($size < 2097152) { + $message .= (string) $this->body; + } + + return $message; + } + + /** + * Get the the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; + $lines = $this->getHeaderLines(); + if (!empty($lines)) { + $headers .= implode("\r\n", $lines) . "\r\n"; + } + + return $headers . "\r\n"; + } + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Get the Accept-Ranges HTTP header + * + * @return string Returns what partial content range types this server supports. + */ + public function getAcceptRanges() + { + return (string) $this->getHeader('Accept-Ranges'); + } + + /** + * Calculate the age of the response + * + * @return integer + */ + public function calculateAge() + { + $age = $this->getHeader('Age'); + + if ($age === null && $this->getDate()) { + $age = time() - strtotime($this->getDate()); + } + + return $age === null ? null : (int) (string) $age; + } + + /** + * Get the Age HTTP header + * + * @return integer|null Returns the age the object has been in a proxy cache in seconds. + */ + public function getAge() + { + return (string) $this->getHeader('Age'); + } + + /** + * Get the Allow HTTP header + * + * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. + */ + public function getAllow() + { + return (string) $this->getHeader('Allow'); + } + + /** + * Check if an HTTP method is allowed by checking the Allow response header + * + * @param string $method Method to check + * + * @return bool + */ + public function isMethodAllowed($method) + { + $allow = $this->getHeader('Allow'); + if ($allow) { + foreach (explode(',', $allow) as $allowable) { + if (!strcasecmp(trim($allowable), $method)) { + return true; + } + } + } + + return false; + } + + /** + * Get the Cache-Control HTTP header + * + * @return string + */ + public function getCacheControl() + { + return (string) $this->getHeader('Cache-Control'); + } + + /** + * Get the Connection HTTP header + * + * @return string + */ + public function getConnection() + { + return (string) $this->getHeader('Connection'); + } + + /** + * Get the Content-Encoding HTTP header + * + * @return string|null + */ + public function getContentEncoding() + { + return (string) $this->getHeader('Content-Encoding'); + } + + /** + * Get the Content-Language HTTP header + * + * @return string|null Returns the language the content is in. + */ + public function getContentLanguage() + { + return (string) $this->getHeader('Content-Language'); + } + + /** + * Get the Content-Length HTTP header + * + * @return integer Returns the length of the response body in bytes + */ + public function getContentLength() + { + return (int) (string) $this->getHeader('Content-Length'); + } + + /** + * Get the Content-Location HTTP header + * + * @return string|null Returns an alternate location for the returned data (e.g /index.htm) + */ + public function getContentLocation() + { + return (string) $this->getHeader('Content-Location'); + } + + /** + * Get the Content-Disposition HTTP header + * + * @return string|null Returns the Content-Disposition header + */ + public function getContentDisposition() + { + return (string) $this->getHeader('Content-Disposition'); + } + + /** + * Get the Content-MD5 HTTP header + * + * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. + */ + public function getContentMd5() + { + return (string) $this->getHeader('Content-MD5'); + } + + /** + * Get the Content-Range HTTP header + * + * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). + */ + public function getContentRange() + { + return (string) $this->getHeader('Content-Range'); + } + + /** + * Get the Content-Type HTTP header + * + * @return string Returns the mime type of this content. + */ + public function getContentType() + { + return (string) $this->getHeader('Content-Type'); + } + + /** + * Checks if the Content-Type is of a certain type. This is useful if the + * Content-Type header contains charset information and you need to know if + * the Content-Type matches a particular type. + * + * @param string $type Content type to check against + * + * @return bool + */ + public function isContentType($type) + { + return stripos($this->getHeader('Content-Type'), $type) !== false; + } + + /** + * Get the Date HTTP header + * + * @return string|null Returns the date and time that the message was sent. + */ + public function getDate() + { + return (string) $this->getHeader('Date'); + } + + /** + * Get the ETag HTTP header + * + * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. + */ + public function getEtag() + { + return (string) $this->getHeader('ETag'); + } + + /** + * Get the Expires HTTP header + * + * @return string|null Returns the date/time after which the response is considered stale. + */ + public function getExpires() + { + return (string) $this->getHeader('Expires'); + } + + /** + * Get the Last-Modified HTTP header + * + * @return string|null Returns the last modified date for the requested object, in RFC 2822 format + * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) + */ + public function getLastModified() + { + return (string) $this->getHeader('Last-Modified'); + } + + /** + * Get the Location HTTP header + * + * @return string|null Used in redirection, or when a new resource has been created. + */ + public function getLocation() + { + return (string) $this->getHeader('Location'); + } + + /** + * Get the Pragma HTTP header + * + * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along + * the request-response chain. + */ + public function getPragma() + { + return (string) $this->getHeader('Pragma'); + } + + /** + * Get the Proxy-Authenticate HTTP header + * + * @return string|null Authentication to access the proxy (e.g. Basic) + */ + public function getProxyAuthenticate() + { + return (string) $this->getHeader('Proxy-Authenticate'); + } + + /** + * Get the Retry-After HTTP header + * + * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a + * specified period of time. + */ + public function getRetryAfter() + { + return (string) $this->getHeader('Retry-After'); + } + + /** + * Get the Server HTTP header + * + * @return string|null A name for the server + */ + public function getServer() + { + return (string) $this->getHeader('Server'); + } + + /** + * Get the Set-Cookie HTTP header + * + * @return string|null An HTTP cookie. + */ + public function getSetCookie() + { + return (string) $this->getHeader('Set-Cookie'); + } + + /** + * Get the Trailer HTTP header + * + * @return string|null The Trailer general field value indicates that the given set of header fields is present in + * the trailer of a message encoded with chunked transfer-coding. + */ + public function getTrailer() + { + return (string) $this->getHeader('Trailer'); + } + + /** + * Get the Transfer-Encoding HTTP header + * + * @return string|null The form of encoding used to safely transfer the entity to the user + */ + public function getTransferEncoding() + { + return (string) $this->getHeader('Transfer-Encoding'); + } + + /** + * Get the Vary HTTP header + * + * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached + * response can be used rather than requesting a fresh one from the origin server. + */ + public function getVary() + { + return (string) $this->getHeader('Vary'); + } + + /** + * Get the Via HTTP header + * + * @return string|null Informs the client of proxies through which the response was sent. + */ + public function getVia() + { + return (string) $this->getHeader('Via'); + } + + /** + * Get the Warning HTTP header + * + * @return string|null A general warning about possible problems with the entity body + */ + public function getWarning() + { + return (string) $this->getHeader('Warning'); + } + + /** + * Get the WWW-Authenticate HTTP header + * + * @return string|null Indicates the authentication scheme that should be used to access the requested entity + */ + public function getWwwAuthenticate() + { + return (string) $this->getHeader('WWW-Authenticate'); + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return bool + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + public function isError() + { + return $this->isClientError() || $this->isServerError(); + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return bool + */ + public function isInformational() + { + return $this->statusCode < 200; + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return bool + */ + public function isRedirect() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return bool + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Checks if HTTP Status code is Successful (2xx | 304) + * + * @return bool + */ + public function isSuccessful() + { + return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; + } + + /** + * Check if the response can be cached based on the response headers + * + * @return bool Returns TRUE if the response can be cached or false if not + */ + public function canCache() + { + // Check if the response is cacheable based on the code + if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { + return false; + } + + // Make sure a valid body was returned and can be cached + if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) + && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { + return false; + } + + // Never cache no-store resources (this is a private cache, so private + // can be cached) + if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); + } + + /** + * Gets the number of seconds from the current time in which this response is still considered fresh + * + * @return int|null Returns the number of seconds + */ + public function getMaxAge() + { + if ($header = $this->getHeader('Cache-Control')) { + // s-max-age, then max-age, then Expires + if ($age = $header->getDirective('s-maxage')) { + return $age; + } + if ($age = $header->getDirective('max-age')) { + return $age; + } + } + + if ($this->getHeader('Expires')) { + return strtotime($this->getExpires()) - time(); + } + + return null; + } + + /** + * Check if the response is considered fresh. + * + * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the + * response. + * + * @return bool|null + */ + public function isFresh() + { + $fresh = $this->getFreshness(); + + return $fresh === null ? null : $fresh >= 0; + } + + /** + * Check if the response can be validated against the origin server using a conditional GET request. + * + * @return bool + */ + public function canValidate() + { + return $this->getEtag() || $this->getLastModified(); + } + + /** + * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the + * age of the response (max-age - age). + * + * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. + * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL + * result means that no freshness information is available. + * + * @return int + */ + public function getFreshness() + { + $maxAge = $this->getMaxAge(); + $age = $this->calculateAge(); + + return $maxAge && $age ? ($maxAge - $age) : null; + } + + /** + * Parse the JSON response body and return an array + * + * @return array|string|int|bool|float + * @throws RuntimeException if the response body is not in JSON format + */ + public function json() + { + $data = json_decode((string) $this->body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); + } + + return $data === null ? array() : $data; + } + + /** + * Parse the XML response body and return a \SimpleXMLElement. + * + * In order to prevent XXE attacks, this method disables loading external + * entities. If you rely on external entities, then you must parse the + * XML response manually by accessing the response body directly. + * + * @return \SimpleXMLElement + * @throws RuntimeException if the response body is not in XML format + * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html + */ + public function xml() + { + $errorMessage = null; + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + try { + $xml = new \SimpleXMLElement((string) $this->body ?: '', LIBXML_NONET); + if ($error = libxml_get_last_error()) { + $errorMessage = $error->message; + } + } catch (\Exception $e) { + $errorMessage = $e->getMessage(); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + if ($errorMessage) { + throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); + } + + return $xml; + } + + /** + * Get the redirect count of this response + * + * @return int + */ + public function getRedirectCount() + { + return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); + } + + /** + * Set the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @param string $url The effective URL + * + * @return self + */ + public function setEffectiveUrl($url) + { + $this->effectiveUrl = $url; + + return $this; + } + + /** + * Get the effective URL that resulted in this response (e.g. the last redirect URL) + * + * @return string + */ + public function getEffectiveUrl() + { + return $this->effectiveUrl; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getPreviousResponse() + { + Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); + return null; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function setRequest($request) + { + Version::warn(__METHOD__ . ' is deprecated'); + return $this; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function getRequest() + { + Version::warn(__METHOD__ . ' is deprecated'); + return null; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php new file mode 100644 index 000000000..d71586a05 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php @@ -0,0 +1,962 @@ + 'text/vnd.in3d.3dml', + '3g2' => 'video/3gpp2', + '3gp' => 'video/3gpp', + '7z' => 'application/x-7z-compressed', + 'aab' => 'application/x-authorware-bin', + 'aac' => 'audio/x-aac', + 'aam' => 'application/x-authorware-map', + 'aas' => 'application/x-authorware-seg', + 'abw' => 'application/x-abiword', + 'ac' => 'application/pkix-attr-cert', + 'acc' => 'application/vnd.americandynamics.acc', + 'ace' => 'application/x-ace-compressed', + 'acu' => 'application/vnd.acucobol', + 'acutc' => 'application/vnd.acucorp', + 'adp' => 'audio/adpcm', + 'aep' => 'application/vnd.audiograph', + 'afm' => 'application/x-font-type1', + 'afp' => 'application/vnd.ibm.modcap', + 'ahead' => 'application/vnd.ahead.space', + 'ai' => 'application/postscript', + 'aif' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'air' => 'application/vnd.adobe.air-application-installer-package+zip', + 'ait' => 'application/vnd.dvb.ait', + 'ami' => 'application/vnd.amiga.ami', + 'apk' => 'application/vnd.android.package-archive', + 'application' => 'application/x-ms-application', + 'apr' => 'application/vnd.lotus-approach', + 'asa' => 'text/plain', + 'asax' => 'application/octet-stream', + 'asc' => 'application/pgp-signature', + 'ascx' => 'text/plain', + 'asf' => 'video/x-ms-asf', + 'ashx' => 'text/plain', + 'asm' => 'text/x-asm', + 'asmx' => 'text/plain', + 'aso' => 'application/vnd.accpac.simply.aso', + 'asp' => 'text/plain', + 'aspx' => 'text/plain', + 'asx' => 'video/x-ms-asf', + 'atc' => 'application/vnd.acucorp', + 'atom' => 'application/atom+xml', + 'atomcat' => 'application/atomcat+xml', + 'atomsvc' => 'application/atomsvc+xml', + 'atx' => 'application/vnd.antix.game-component', + 'au' => 'audio/basic', + 'avi' => 'video/x-msvideo', + 'aw' => 'application/applixware', + 'axd' => 'text/plain', + 'azf' => 'application/vnd.airzip.filesecure.azf', + 'azs' => 'application/vnd.airzip.filesecure.azs', + 'azw' => 'application/vnd.amazon.ebook', + 'bat' => 'application/x-msdownload', + 'bcpio' => 'application/x-bcpio', + 'bdf' => 'application/x-font-bdf', + 'bdm' => 'application/vnd.syncml.dm+wbxml', + 'bed' => 'application/vnd.realvnc.bed', + 'bh2' => 'application/vnd.fujitsu.oasysprs', + 'bin' => 'application/octet-stream', + 'bmi' => 'application/vnd.bmi', + 'bmp' => 'image/bmp', + 'book' => 'application/vnd.framemaker', + 'box' => 'application/vnd.previewsystems.box', + 'boz' => 'application/x-bzip2', + 'bpk' => 'application/octet-stream', + 'btif' => 'image/prs.btif', + 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', + 'c' => 'text/x-c', + 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', + 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', + 'c4d' => 'application/vnd.clonk.c4group', + 'c4f' => 'application/vnd.clonk.c4group', + 'c4g' => 'application/vnd.clonk.c4group', + 'c4p' => 'application/vnd.clonk.c4group', + 'c4u' => 'application/vnd.clonk.c4group', + 'cab' => 'application/vnd.ms-cab-compressed', + 'car' => 'application/vnd.curl.car', + 'cat' => 'application/vnd.ms-pki.seccat', + 'cc' => 'text/x-c', + 'cct' => 'application/x-director', + 'ccxml' => 'application/ccxml+xml', + 'cdbcmsg' => 'application/vnd.contact.cmsg', + 'cdf' => 'application/x-netcdf', + 'cdkey' => 'application/vnd.mediastation.cdkey', + 'cdmia' => 'application/cdmi-capability', + 'cdmic' => 'application/cdmi-container', + 'cdmid' => 'application/cdmi-domain', + 'cdmio' => 'application/cdmi-object', + 'cdmiq' => 'application/cdmi-queue', + 'cdx' => 'chemical/x-cdx', + 'cdxml' => 'application/vnd.chemdraw+xml', + 'cdy' => 'application/vnd.cinderella', + 'cer' => 'application/pkix-cert', + 'cfc' => 'application/x-coldfusion', + 'cfm' => 'application/x-coldfusion', + 'cgm' => 'image/cgm', + 'chat' => 'application/x-chat', + 'chm' => 'application/vnd.ms-htmlhelp', + 'chrt' => 'application/vnd.kde.kchart', + 'cif' => 'chemical/x-cif', + 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', + 'cil' => 'application/vnd.ms-artgalry', + 'cla' => 'application/vnd.claymore', + 'class' => 'application/java-vm', + 'clkk' => 'application/vnd.crick.clicker.keyboard', + 'clkp' => 'application/vnd.crick.clicker.palette', + 'clkt' => 'application/vnd.crick.clicker.template', + 'clkw' => 'application/vnd.crick.clicker.wordbank', + 'clkx' => 'application/vnd.crick.clicker', + 'clp' => 'application/x-msclip', + 'cmc' => 'application/vnd.cosmocaller', + 'cmdf' => 'chemical/x-cmdf', + 'cml' => 'chemical/x-cml', + 'cmp' => 'application/vnd.yellowriver-custom-menu', + 'cmx' => 'image/x-cmx', + 'cod' => 'application/vnd.rim.cod', + 'com' => 'application/x-msdownload', + 'conf' => 'text/plain', + 'cpio' => 'application/x-cpio', + 'cpp' => 'text/x-c', + 'cpt' => 'application/mac-compactpro', + 'crd' => 'application/x-mscardfile', + 'crl' => 'application/pkix-crl', + 'crt' => 'application/x-x509-ca-cert', + 'cryptonote' => 'application/vnd.rig.cryptonote', + 'cs' => 'text/plain', + 'csh' => 'application/x-csh', + 'csml' => 'chemical/x-csml', + 'csp' => 'application/vnd.commonspace', + 'css' => 'text/css', + 'cst' => 'application/x-director', + 'csv' => 'text/csv', + 'cu' => 'application/cu-seeme', + 'curl' => 'text/vnd.curl', + 'cww' => 'application/prs.cww', + 'cxt' => 'application/x-director', + 'cxx' => 'text/x-c', + 'dae' => 'model/vnd.collada+xml', + 'daf' => 'application/vnd.mobius.daf', + 'dataless' => 'application/vnd.fdsn.seed', + 'davmount' => 'application/davmount+xml', + 'dcr' => 'application/x-director', + 'dcurl' => 'text/vnd.curl.dcurl', + 'dd2' => 'application/vnd.oma.dd2+xml', + 'ddd' => 'application/vnd.fujixerox.ddd', + 'deb' => 'application/x-debian-package', + 'def' => 'text/plain', + 'deploy' => 'application/octet-stream', + 'der' => 'application/x-x509-ca-cert', + 'dfac' => 'application/vnd.dreamfactory', + 'dic' => 'text/x-c', + 'dir' => 'application/x-director', + 'dis' => 'application/vnd.mobius.dis', + 'dist' => 'application/octet-stream', + 'distz' => 'application/octet-stream', + 'djv' => 'image/vnd.djvu', + 'djvu' => 'image/vnd.djvu', + 'dll' => 'application/x-msdownload', + 'dmg' => 'application/octet-stream', + 'dms' => 'application/octet-stream', + 'dna' => 'application/vnd.dna', + 'doc' => 'application/msword', + 'docm' => 'application/vnd.ms-word.document.macroenabled.12', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dot' => 'application/msword', + 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'dp' => 'application/vnd.osgi.dp', + 'dpg' => 'application/vnd.dpgraph', + 'dra' => 'audio/vnd.dra', + 'dsc' => 'text/prs.lines.tag', + 'dssc' => 'application/dssc+der', + 'dtb' => 'application/x-dtbook+xml', + 'dtd' => 'application/xml-dtd', + 'dts' => 'audio/vnd.dts', + 'dtshd' => 'audio/vnd.dts.hd', + 'dump' => 'application/octet-stream', + 'dvi' => 'application/x-dvi', + 'dwf' => 'model/vnd.dwf', + 'dwg' => 'image/vnd.dwg', + 'dxf' => 'image/vnd.dxf', + 'dxp' => 'application/vnd.spotfire.dxp', + 'dxr' => 'application/x-director', + 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', + 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', + 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', + 'ecma' => 'application/ecmascript', + 'edm' => 'application/vnd.novadigm.edm', + 'edx' => 'application/vnd.novadigm.edx', + 'efif' => 'application/vnd.picsel', + 'ei6' => 'application/vnd.pg.osasli', + 'elc' => 'application/octet-stream', + 'eml' => 'message/rfc822', + 'emma' => 'application/emma+xml', + 'eol' => 'audio/vnd.digital-winds', + 'eot' => 'application/vnd.ms-fontobject', + 'eps' => 'application/postscript', + 'epub' => 'application/epub+zip', + 'es3' => 'application/vnd.eszigno3+xml', + 'esf' => 'application/vnd.epson.esf', + 'et3' => 'application/vnd.eszigno3+xml', + 'etx' => 'text/x-setext', + 'exe' => 'application/x-msdownload', + 'exi' => 'application/exi', + 'ext' => 'application/vnd.novadigm.ext', + 'ez' => 'application/andrew-inset', + 'ez2' => 'application/vnd.ezpix-album', + 'ez3' => 'application/vnd.ezpix-package', + 'f' => 'text/x-fortran', + 'f4v' => 'video/x-f4v', + 'f77' => 'text/x-fortran', + 'f90' => 'text/x-fortran', + 'fbs' => 'image/vnd.fastbidsheet', + 'fcs' => 'application/vnd.isac.fcs', + 'fdf' => 'application/vnd.fdf', + 'fe_launch' => 'application/vnd.denovo.fcselayout-link', + 'fg5' => 'application/vnd.fujitsu.oasysgp', + 'fgd' => 'application/x-director', + 'fh' => 'image/x-freehand', + 'fh4' => 'image/x-freehand', + 'fh5' => 'image/x-freehand', + 'fh7' => 'image/x-freehand', + 'fhc' => 'image/x-freehand', + 'fig' => 'application/x-xfig', + 'fli' => 'video/x-fli', + 'flo' => 'application/vnd.micrografx.flo', + 'flv' => 'video/x-flv', + 'flw' => 'application/vnd.kde.kivio', + 'flx' => 'text/vnd.fmi.flexstor', + 'fly' => 'text/vnd.fly', + 'fm' => 'application/vnd.framemaker', + 'fnc' => 'application/vnd.frogans.fnc', + 'for' => 'text/x-fortran', + 'fpx' => 'image/vnd.fpx', + 'frame' => 'application/vnd.framemaker', + 'fsc' => 'application/vnd.fsc.weblaunch', + 'fst' => 'image/vnd.fst', + 'ftc' => 'application/vnd.fluxtime.clip', + 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', + 'fvt' => 'video/vnd.fvt', + 'fxp' => 'application/vnd.adobe.fxp', + 'fxpl' => 'application/vnd.adobe.fxp', + 'fzs' => 'application/vnd.fuzzysheet', + 'g2w' => 'application/vnd.geoplan', + 'g3' => 'image/g3fax', + 'g3w' => 'application/vnd.geospace', + 'gac' => 'application/vnd.groove-account', + 'gdl' => 'model/vnd.gdl', + 'geo' => 'application/vnd.dynageo', + 'gex' => 'application/vnd.geometry-explorer', + 'ggb' => 'application/vnd.geogebra.file', + 'ggt' => 'application/vnd.geogebra.tool', + 'ghf' => 'application/vnd.groove-help', + 'gif' => 'image/gif', + 'gim' => 'application/vnd.groove-identity-message', + 'gmx' => 'application/vnd.gmx', + 'gnumeric' => 'application/x-gnumeric', + 'gph' => 'application/vnd.flographit', + 'gqf' => 'application/vnd.grafeq', + 'gqs' => 'application/vnd.grafeq', + 'gram' => 'application/srgs', + 'gre' => 'application/vnd.geometry-explorer', + 'grv' => 'application/vnd.groove-injector', + 'grxml' => 'application/srgs+xml', + 'gsf' => 'application/x-font-ghostscript', + 'gtar' => 'application/x-gtar', + 'gtm' => 'application/vnd.groove-tool-message', + 'gtw' => 'model/vnd.gtw', + 'gv' => 'text/vnd.graphviz', + 'gxt' => 'application/vnd.geonext', + 'h' => 'text/x-c', + 'h261' => 'video/h261', + 'h263' => 'video/h263', + 'h264' => 'video/h264', + 'hal' => 'application/vnd.hal+xml', + 'hbci' => 'application/vnd.hbci', + 'hdf' => 'application/x-hdf', + 'hh' => 'text/x-c', + 'hlp' => 'application/winhlp', + 'hpgl' => 'application/vnd.hp-hpgl', + 'hpid' => 'application/vnd.hp-hpid', + 'hps' => 'application/vnd.hp-hps', + 'hqx' => 'application/mac-binhex40', + 'hta' => 'application/octet-stream', + 'htc' => 'text/html', + 'htke' => 'application/vnd.kenameaapp', + 'htm' => 'text/html', + 'html' => 'text/html', + 'hvd' => 'application/vnd.yamaha.hv-dic', + 'hvp' => 'application/vnd.yamaha.hv-voice', + 'hvs' => 'application/vnd.yamaha.hv-script', + 'i2g' => 'application/vnd.intergeo', + 'icc' => 'application/vnd.iccprofile', + 'ice' => 'x-conference/x-cooltalk', + 'icm' => 'application/vnd.iccprofile', + 'ico' => 'image/x-icon', + 'ics' => 'text/calendar', + 'ief' => 'image/ief', + 'ifb' => 'text/calendar', + 'ifm' => 'application/vnd.shana.informed.formdata', + 'iges' => 'model/iges', + 'igl' => 'application/vnd.igloader', + 'igm' => 'application/vnd.insors.igm', + 'igs' => 'model/iges', + 'igx' => 'application/vnd.micrografx.igx', + 'iif' => 'application/vnd.shana.informed.interchange', + 'imp' => 'application/vnd.accpac.simply.imp', + 'ims' => 'application/vnd.ms-ims', + 'in' => 'text/plain', + 'ini' => 'text/plain', + 'ipfix' => 'application/ipfix', + 'ipk' => 'application/vnd.shana.informed.package', + 'irm' => 'application/vnd.ibm.rights-management', + 'irp' => 'application/vnd.irepository.package+xml', + 'iso' => 'application/octet-stream', + 'itp' => 'application/vnd.shana.informed.formtemplate', + 'ivp' => 'application/vnd.immervision-ivp', + 'ivu' => 'application/vnd.immervision-ivu', + 'jad' => 'text/vnd.sun.j2me.app-descriptor', + 'jam' => 'application/vnd.jam', + 'jar' => 'application/java-archive', + 'java' => 'text/x-java-source', + 'jisp' => 'application/vnd.jisp', + 'jlt' => 'application/vnd.hp-jlyt', + 'jnlp' => 'application/x-java-jnlp-file', + 'joda' => 'application/vnd.joost.joda-archive', + 'jpe' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpgm' => 'video/jpm', + 'jpgv' => 'video/jpeg', + 'jpm' => 'video/jpm', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'kar' => 'audio/midi', + 'karbon' => 'application/vnd.kde.karbon', + 'kfo' => 'application/vnd.kde.kformula', + 'kia' => 'application/vnd.kidspiration', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'kne' => 'application/vnd.kinar', + 'knp' => 'application/vnd.kinar', + 'kon' => 'application/vnd.kde.kontour', + 'kpr' => 'application/vnd.kde.kpresenter', + 'kpt' => 'application/vnd.kde.kpresenter', + 'ksp' => 'application/vnd.kde.kspread', + 'ktr' => 'application/vnd.kahootz', + 'ktx' => 'image/ktx', + 'ktz' => 'application/vnd.kahootz', + 'kwd' => 'application/vnd.kde.kword', + 'kwt' => 'application/vnd.kde.kword', + 'lasxml' => 'application/vnd.las.las+xml', + 'latex' => 'application/x-latex', + 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', + 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', + 'les' => 'application/vnd.hhe.lesson-player', + 'lha' => 'application/octet-stream', + 'link66' => 'application/vnd.route66.link66+xml', + 'list' => 'text/plain', + 'list3820' => 'application/vnd.ibm.modcap', + 'listafp' => 'application/vnd.ibm.modcap', + 'log' => 'text/plain', + 'lostxml' => 'application/lost+xml', + 'lrf' => 'application/octet-stream', + 'lrm' => 'application/vnd.ms-lrm', + 'ltf' => 'application/vnd.frogans.ltf', + 'lvp' => 'audio/vnd.lucent.voice', + 'lwp' => 'application/vnd.lotus-wordpro', + 'lzh' => 'application/octet-stream', + 'm13' => 'application/x-msmediaview', + 'm14' => 'application/x-msmediaview', + 'm1v' => 'video/mpeg', + 'm21' => 'application/mp21', + 'm2a' => 'audio/mpeg', + 'm2v' => 'video/mpeg', + 'm3a' => 'audio/mpeg', + 'm3u' => 'audio/x-mpegurl', + 'm3u8' => 'application/vnd.apple.mpegurl', + 'm4a' => 'audio/mp4', + 'm4u' => 'video/vnd.mpegurl', + 'm4v' => 'video/mp4', + 'ma' => 'application/mathematica', + 'mads' => 'application/mads+xml', + 'mag' => 'application/vnd.ecowin.chart', + 'maker' => 'application/vnd.framemaker', + 'man' => 'text/troff', + 'mathml' => 'application/mathml+xml', + 'mb' => 'application/mathematica', + 'mbk' => 'application/vnd.mobius.mbk', + 'mbox' => 'application/mbox', + 'mc1' => 'application/vnd.medcalcdata', + 'mcd' => 'application/vnd.mcd', + 'mcurl' => 'text/vnd.curl.mcurl', + 'mdb' => 'application/x-msaccess', + 'mdi' => 'image/vnd.ms-modi', + 'me' => 'text/troff', + 'mesh' => 'model/mesh', + 'meta4' => 'application/metalink4+xml', + 'mets' => 'application/mets+xml', + 'mfm' => 'application/vnd.mfmp', + 'mgp' => 'application/vnd.osgeo.mapguide.package', + 'mgz' => 'application/vnd.proteus.magazine', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mif' => 'application/vnd.mif', + 'mime' => 'message/rfc822', + 'mj2' => 'video/mj2', + 'mjp2' => 'video/mj2', + 'mlp' => 'application/vnd.dolby.mlp', + 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', + 'mmf' => 'application/vnd.smaf', + 'mmr' => 'image/vnd.fujixerox.edmics-mmr', + 'mny' => 'application/x-msmoney', + 'mobi' => 'application/x-mobipocket-ebook', + 'mods' => 'application/mods+xml', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'mp2' => 'audio/mpeg', + 'mp21' => 'application/mp21', + 'mp2a' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'mp4' => 'video/mp4', + 'mp4a' => 'audio/mp4', + 'mp4s' => 'application/mp4', + 'mp4v' => 'video/mp4', + 'mpc' => 'application/vnd.mophun.certificate', + 'mpe' => 'video/mpeg', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpg4' => 'video/mp4', + 'mpga' => 'audio/mpeg', + 'mpkg' => 'application/vnd.apple.installer+xml', + 'mpm' => 'application/vnd.blueice.multipass', + 'mpn' => 'application/vnd.mophun.application', + 'mpp' => 'application/vnd.ms-project', + 'mpt' => 'application/vnd.ms-project', + 'mpy' => 'application/vnd.ibm.minipay', + 'mqy' => 'application/vnd.mobius.mqy', + 'mrc' => 'application/marc', + 'mrcx' => 'application/marcxml+xml', + 'ms' => 'text/troff', + 'mscml' => 'application/mediaservercontrol+xml', + 'mseed' => 'application/vnd.fdsn.mseed', + 'mseq' => 'application/vnd.mseq', + 'msf' => 'application/vnd.epson.msf', + 'msh' => 'model/mesh', + 'msi' => 'application/x-msdownload', + 'msl' => 'application/vnd.mobius.msl', + 'msty' => 'application/vnd.muvee.style', + 'mts' => 'model/vnd.mts', + 'mus' => 'application/vnd.musician', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + 'mvb' => 'application/x-msmediaview', + 'mwf' => 'application/vnd.mfer', + 'mxf' => 'application/mxf', + 'mxl' => 'application/vnd.recordare.musicxml', + 'mxml' => 'application/xv+xml', + 'mxs' => 'application/vnd.triscape.mxs', + 'mxu' => 'video/vnd.mpegurl', + 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', + 'n3' => 'text/n3', + 'nb' => 'application/mathematica', + 'nbp' => 'application/vnd.wolfram.player', + 'nc' => 'application/x-netcdf', + 'ncx' => 'application/x-dtbncx+xml', + 'ngdat' => 'application/vnd.nokia.n-gage.data', + 'nlu' => 'application/vnd.neurolanguage.nlu', + 'nml' => 'application/vnd.enliven', + 'nnd' => 'application/vnd.noblenet-directory', + 'nns' => 'application/vnd.noblenet-sealer', + 'nnw' => 'application/vnd.noblenet-web', + 'npx' => 'image/vnd.net-fpx', + 'nsf' => 'application/vnd.lotus-notes', + 'oa2' => 'application/vnd.fujitsu.oasys2', + 'oa3' => 'application/vnd.fujitsu.oasys3', + 'oas' => 'application/vnd.fujitsu.oasys', + 'obd' => 'application/x-msbinder', + 'oda' => 'application/oda', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odft' => 'application/vnd.oasis.opendocument.formula-template', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'oga' => 'audio/ogg', + 'ogg' => 'audio/ogg', + 'ogv' => 'video/ogg', + 'ogx' => 'application/ogg', + 'onepkg' => 'application/onenote', + 'onetmp' => 'application/onenote', + 'onetoc' => 'application/onenote', + 'onetoc2' => 'application/onenote', + 'opf' => 'application/oebps-package+xml', + 'oprc' => 'application/vnd.palm', + 'org' => 'application/vnd.lotus-organizer', + 'osf' => 'application/vnd.yamaha.openscoreformat', + 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', + 'otc' => 'application/vnd.oasis.opendocument.chart-template', + 'otf' => 'application/x-font-otf', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'oti' => 'application/vnd.oasis.opendocument.image-template', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oxt' => 'application/vnd.openofficeorg.extension', + 'p' => 'text/x-pascal', + 'p10' => 'application/pkcs10', + 'p12' => 'application/x-pkcs12', + 'p7b' => 'application/x-pkcs7-certificates', + 'p7c' => 'application/pkcs7-mime', + 'p7m' => 'application/pkcs7-mime', + 'p7r' => 'application/x-pkcs7-certreqresp', + 'p7s' => 'application/pkcs7-signature', + 'p8' => 'application/pkcs8', + 'pas' => 'text/x-pascal', + 'paw' => 'application/vnd.pawaafile', + 'pbd' => 'application/vnd.powerbuilder6', + 'pbm' => 'image/x-portable-bitmap', + 'pcf' => 'application/x-font-pcf', + 'pcl' => 'application/vnd.hp-pcl', + 'pclxl' => 'application/vnd.hp-pclxl', + 'pct' => 'image/x-pict', + 'pcurl' => 'application/vnd.curl.pcurl', + 'pcx' => 'image/x-pcx', + 'pdb' => 'application/vnd.palm', + 'pdf' => 'application/pdf', + 'pfa' => 'application/x-font-type1', + 'pfb' => 'application/x-font-type1', + 'pfm' => 'application/x-font-type1', + 'pfr' => 'application/font-tdpfr', + 'pfx' => 'application/x-pkcs12', + 'pgm' => 'image/x-portable-graymap', + 'pgn' => 'application/x-chess-pgn', + 'pgp' => 'application/pgp-encrypted', + 'php' => 'text/x-php', + 'phps' => 'application/x-httpd-phps', + 'pic' => 'image/x-pict', + 'pkg' => 'application/octet-stream', + 'pki' => 'application/pkixcmp', + 'pkipath' => 'application/pkix-pkipath', + 'plb' => 'application/vnd.3gpp.pic-bw-large', + 'plc' => 'application/vnd.mobius.plc', + 'plf' => 'application/vnd.pocketlearn', + 'pls' => 'application/pls+xml', + 'pml' => 'application/vnd.ctc-posml', + 'png' => 'image/png', + 'pnm' => 'image/x-portable-anymap', + 'portpkg' => 'application/vnd.macports.portpkg', + 'pot' => 'application/vnd.ms-powerpoint', + 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', + 'ppd' => 'application/vnd.cups-ppd', + 'ppm' => 'image/x-portable-pixmap', + 'pps' => 'application/vnd.ms-powerpoint', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'pqa' => 'application/vnd.palm', + 'prc' => 'application/x-mobipocket-ebook', + 'pre' => 'application/vnd.lotus-freelance', + 'prf' => 'application/pics-rules', + 'ps' => 'application/postscript', + 'psb' => 'application/vnd.3gpp.pic-bw-small', + 'psd' => 'image/vnd.adobe.photoshop', + 'psf' => 'application/x-font-linux-psf', + 'pskcxml' => 'application/pskc+xml', + 'ptid' => 'application/vnd.pvi.ptid1', + 'pub' => 'application/x-mspublisher', + 'pvb' => 'application/vnd.3gpp.pic-bw-var', + 'pwn' => 'application/vnd.3m.post-it-notes', + 'pya' => 'audio/vnd.ms-playready.media.pya', + 'pyv' => 'video/vnd.ms-playready.media.pyv', + 'qam' => 'application/vnd.epson.quickanime', + 'qbo' => 'application/vnd.intu.qbo', + 'qfx' => 'application/vnd.intu.qfx', + 'qps' => 'application/vnd.publishare-delta-tree', + 'qt' => 'video/quicktime', + 'qwd' => 'application/vnd.quark.quarkxpress', + 'qwt' => 'application/vnd.quark.quarkxpress', + 'qxb' => 'application/vnd.quark.quarkxpress', + 'qxd' => 'application/vnd.quark.quarkxpress', + 'qxl' => 'application/vnd.quark.quarkxpress', + 'qxt' => 'application/vnd.quark.quarkxpress', + 'ra' => 'audio/x-pn-realaudio', + 'ram' => 'audio/x-pn-realaudio', + 'rar' => 'application/x-rar-compressed', + 'ras' => 'image/x-cmu-raster', + 'rb' => 'text/plain', + 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', + 'rdf' => 'application/rdf+xml', + 'rdz' => 'application/vnd.data-vision.rdz', + 'rep' => 'application/vnd.businessobjects', + 'res' => 'application/x-dtbresource+xml', + 'resx' => 'text/xml', + 'rgb' => 'image/x-rgb', + 'rif' => 'application/reginfo+xml', + 'rip' => 'audio/vnd.rip', + 'rl' => 'application/resource-lists+xml', + 'rlc' => 'image/vnd.fujixerox.edmics-rlc', + 'rld' => 'application/resource-lists-diff+xml', + 'rm' => 'application/vnd.rn-realmedia', + 'rmi' => 'audio/midi', + 'rmp' => 'audio/x-pn-realaudio-plugin', + 'rms' => 'application/vnd.jcp.javame.midlet-rms', + 'rnc' => 'application/relax-ng-compact-syntax', + 'roff' => 'text/troff', + 'rp9' => 'application/vnd.cloanto.rp9', + 'rpss' => 'application/vnd.nokia.radio-presets', + 'rpst' => 'application/vnd.nokia.radio-preset', + 'rq' => 'application/sparql-query', + 'rs' => 'application/rls-services+xml', + 'rsd' => 'application/rsd+xml', + 'rss' => 'application/rss+xml', + 'rtf' => 'application/rtf', + 'rtx' => 'text/richtext', + 's' => 'text/x-asm', + 'saf' => 'application/vnd.yamaha.smaf-audio', + 'sbml' => 'application/sbml+xml', + 'sc' => 'application/vnd.ibm.secure-container', + 'scd' => 'application/x-msschedule', + 'scm' => 'application/vnd.lotus-screencam', + 'scq' => 'application/scvp-cv-request', + 'scs' => 'application/scvp-cv-response', + 'scurl' => 'text/vnd.curl.scurl', + 'sda' => 'application/vnd.stardivision.draw', + 'sdc' => 'application/vnd.stardivision.calc', + 'sdd' => 'application/vnd.stardivision.impress', + 'sdkd' => 'application/vnd.solent.sdkm+xml', + 'sdkm' => 'application/vnd.solent.sdkm+xml', + 'sdp' => 'application/sdp', + 'sdw' => 'application/vnd.stardivision.writer', + 'see' => 'application/vnd.seemail', + 'seed' => 'application/vnd.fdsn.seed', + 'sema' => 'application/vnd.sema', + 'semd' => 'application/vnd.semd', + 'semf' => 'application/vnd.semf', + 'ser' => 'application/java-serialized-object', + 'setpay' => 'application/set-payment-initiation', + 'setreg' => 'application/set-registration-initiation', + 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', + 'sfs' => 'application/vnd.spotfire.sfs', + 'sgl' => 'application/vnd.stardivision.writer-global', + 'sgm' => 'text/sgml', + 'sgml' => 'text/sgml', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'shf' => 'application/shf+xml', + 'sig' => 'application/pgp-signature', + 'silo' => 'model/mesh', + 'sis' => 'application/vnd.symbian.install', + 'sisx' => 'application/vnd.symbian.install', + 'sit' => 'application/x-stuffit', + 'sitx' => 'application/x-stuffitx', + 'skd' => 'application/vnd.koan', + 'skm' => 'application/vnd.koan', + 'skp' => 'application/vnd.koan', + 'skt' => 'application/vnd.koan', + 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', + 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', + 'slt' => 'application/vnd.epson.salt', + 'sm' => 'application/vnd.stepmania.stepchart', + 'smf' => 'application/vnd.stardivision.math', + 'smi' => 'application/smil+xml', + 'smil' => 'application/smil+xml', + 'snd' => 'audio/basic', + 'snf' => 'application/x-font-snf', + 'so' => 'application/octet-stream', + 'spc' => 'application/x-pkcs7-certificates', + 'spf' => 'application/vnd.yamaha.smaf-phrase', + 'spl' => 'application/x-futuresplash', + 'spot' => 'text/vnd.in3d.spot', + 'spp' => 'application/scvp-vp-response', + 'spq' => 'application/scvp-vp-request', + 'spx' => 'audio/ogg', + 'src' => 'application/x-wais-source', + 'sru' => 'application/sru+xml', + 'srx' => 'application/sparql-results+xml', + 'sse' => 'application/vnd.kodak-descriptor', + 'ssf' => 'application/vnd.epson.ssf', + 'ssml' => 'application/ssml+xml', + 'st' => 'application/vnd.sailingtracker.track', + 'stc' => 'application/vnd.sun.xml.calc.template', + 'std' => 'application/vnd.sun.xml.draw.template', + 'stf' => 'application/vnd.wt.stf', + 'sti' => 'application/vnd.sun.xml.impress.template', + 'stk' => 'application/hyperstudio', + 'stl' => 'application/vnd.ms-pki.stl', + 'str' => 'application/vnd.pg.format', + 'stw' => 'application/vnd.sun.xml.writer.template', + 'sub' => 'image/vnd.dvb.subtitle', + 'sus' => 'application/vnd.sus-calendar', + 'susp' => 'application/vnd.sus-calendar', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'svc' => 'application/vnd.dvb.service', + 'svd' => 'application/vnd.svd', + 'svg' => 'image/svg+xml', + 'svgz' => 'image/svg+xml', + 'swa' => 'application/x-director', + 'swf' => 'application/x-shockwave-flash', + 'swi' => 'application/vnd.aristanetworks.swi', + 'sxc' => 'application/vnd.sun.xml.calc', + 'sxd' => 'application/vnd.sun.xml.draw', + 'sxg' => 'application/vnd.sun.xml.writer.global', + 'sxi' => 'application/vnd.sun.xml.impress', + 'sxm' => 'application/vnd.sun.xml.math', + 'sxw' => 'application/vnd.sun.xml.writer', + 't' => 'text/troff', + 'tao' => 'application/vnd.tao.intent-module-archive', + 'tar' => 'application/x-tar', + 'tcap' => 'application/vnd.3gpp2.tcap', + 'tcl' => 'application/x-tcl', + 'teacher' => 'application/vnd.smart.teacher', + 'tei' => 'application/tei+xml', + 'teicorpus' => 'application/tei+xml', + 'tex' => 'application/x-tex', + 'texi' => 'application/x-texinfo', + 'texinfo' => 'application/x-texinfo', + 'text' => 'text/plain', + 'tfi' => 'application/thraud+xml', + 'tfm' => 'application/x-tex-tfm', + 'thmx' => 'application/vnd.ms-officetheme', + 'tif' => 'image/tiff', + 'tiff' => 'image/tiff', + 'tmo' => 'application/vnd.tmobile-livetv', + 'torrent' => 'application/x-bittorrent', + 'tpl' => 'application/vnd.groove-tool-template', + 'tpt' => 'application/vnd.trid.tpt', + 'tr' => 'text/troff', + 'tra' => 'application/vnd.trueapp', + 'trm' => 'application/x-msterminal', + 'tsd' => 'application/timestamped-data', + 'tsv' => 'text/tab-separated-values', + 'ttc' => 'application/x-font-ttf', + 'ttf' => 'application/x-font-ttf', + 'ttl' => 'text/turtle', + 'twd' => 'application/vnd.simtech-mindmapper', + 'twds' => 'application/vnd.simtech-mindmapper', + 'txd' => 'application/vnd.genomatix.tuxedo', + 'txf' => 'application/vnd.mobius.txf', + 'txt' => 'text/plain', + 'u32' => 'application/x-authorware-bin', + 'udeb' => 'application/x-debian-package', + 'ufd' => 'application/vnd.ufdl', + 'ufdl' => 'application/vnd.ufdl', + 'umj' => 'application/vnd.umajin', + 'unityweb' => 'application/vnd.unity', + 'uoml' => 'application/vnd.uoml+xml', + 'uri' => 'text/uri-list', + 'uris' => 'text/uri-list', + 'urls' => 'text/uri-list', + 'ustar' => 'application/x-ustar', + 'utz' => 'application/vnd.uiq.theme', + 'uu' => 'text/x-uuencode', + 'uva' => 'audio/vnd.dece.audio', + 'uvd' => 'application/vnd.dece.data', + 'uvf' => 'application/vnd.dece.data', + 'uvg' => 'image/vnd.dece.graphic', + 'uvh' => 'video/vnd.dece.hd', + 'uvi' => 'image/vnd.dece.graphic', + 'uvm' => 'video/vnd.dece.mobile', + 'uvp' => 'video/vnd.dece.pd', + 'uvs' => 'video/vnd.dece.sd', + 'uvt' => 'application/vnd.dece.ttml+xml', + 'uvu' => 'video/vnd.uvvu.mp4', + 'uvv' => 'video/vnd.dece.video', + 'uvva' => 'audio/vnd.dece.audio', + 'uvvd' => 'application/vnd.dece.data', + 'uvvf' => 'application/vnd.dece.data', + 'uvvg' => 'image/vnd.dece.graphic', + 'uvvh' => 'video/vnd.dece.hd', + 'uvvi' => 'image/vnd.dece.graphic', + 'uvvm' => 'video/vnd.dece.mobile', + 'uvvp' => 'video/vnd.dece.pd', + 'uvvs' => 'video/vnd.dece.sd', + 'uvvt' => 'application/vnd.dece.ttml+xml', + 'uvvu' => 'video/vnd.uvvu.mp4', + 'uvvv' => 'video/vnd.dece.video', + 'uvvx' => 'application/vnd.dece.unspecified', + 'uvx' => 'application/vnd.dece.unspecified', + 'vcd' => 'application/x-cdlink', + 'vcf' => 'text/x-vcard', + 'vcg' => 'application/vnd.groove-vcard', + 'vcs' => 'text/x-vcalendar', + 'vcx' => 'application/vnd.vcx', + 'vis' => 'application/vnd.visionary', + 'viv' => 'video/vnd.vivo', + 'vor' => 'application/vnd.stardivision.writer', + 'vox' => 'application/x-authorware-bin', + 'vrml' => 'model/vrml', + 'vsd' => 'application/vnd.visio', + 'vsf' => 'application/vnd.vsf', + 'vss' => 'application/vnd.visio', + 'vst' => 'application/vnd.visio', + 'vsw' => 'application/vnd.visio', + 'vtu' => 'model/vnd.vtu', + 'vxml' => 'application/voicexml+xml', + 'w3d' => 'application/x-director', + 'wad' => 'application/x-doom', + 'wav' => 'audio/x-wav', + 'wax' => 'audio/x-ms-wax', + 'wbmp' => 'image/vnd.wap.wbmp', + 'wbs' => 'application/vnd.criticaltools.wbs+xml', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wcm' => 'application/vnd.ms-works', + 'wdb' => 'application/vnd.ms-works', + 'weba' => 'audio/webm', + 'webm' => 'video/webm', + 'webp' => 'image/webp', + 'wg' => 'application/vnd.pmi.widget', + 'wgt' => 'application/widget', + 'wks' => 'application/vnd.ms-works', + 'wm' => 'video/x-ms-wm', + 'wma' => 'audio/x-ms-wma', + 'wmd' => 'application/x-ms-wmd', + 'wmf' => 'application/x-msmetafile', + 'wml' => 'text/vnd.wap.wml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmls' => 'text/vnd.wap.wmlscript', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'wmv' => 'video/x-ms-wmv', + 'wmx' => 'video/x-ms-wmx', + 'wmz' => 'application/x-ms-wmz', + 'woff' => 'application/x-font-woff', + 'wpd' => 'application/vnd.wordperfect', + 'wpl' => 'application/vnd.ms-wpl', + 'wps' => 'application/vnd.ms-works', + 'wqd' => 'application/vnd.wqd', + 'wri' => 'application/x-mswrite', + 'wrl' => 'model/vrml', + 'wsdl' => 'application/wsdl+xml', + 'wspolicy' => 'application/wspolicy+xml', + 'wtb' => 'application/vnd.webturbo', + 'wvx' => 'video/x-ms-wvx', + 'x32' => 'application/x-authorware-bin', + 'x3d' => 'application/vnd.hzn-3d-crossword', + 'xap' => 'application/x-silverlight-app', + 'xar' => 'application/vnd.xara', + 'xbap' => 'application/x-ms-xbap', + 'xbd' => 'application/vnd.fujixerox.docuworks.binder', + 'xbm' => 'image/x-xbitmap', + 'xdf' => 'application/xcap-diff+xml', + 'xdm' => 'application/vnd.syncml.dm+xml', + 'xdp' => 'application/vnd.adobe.xdp+xml', + 'xdssc' => 'application/dssc+xml', + 'xdw' => 'application/vnd.fujixerox.docuworks', + 'xenc' => 'application/xenc+xml', + 'xer' => 'application/patch-ops-error+xml', + 'xfdf' => 'application/vnd.adobe.xfdf', + 'xfdl' => 'application/vnd.xfdl', + 'xht' => 'application/xhtml+xml', + 'xhtml' => 'application/xhtml+xml', + 'xhvml' => 'application/xv+xml', + 'xif' => 'image/vnd.xiff', + 'xla' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', + 'xlc' => 'application/vnd.ms-excel', + 'xlm' => 'application/vnd.ms-excel', + 'xls' => 'application/vnd.ms-excel', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xlt' => 'application/vnd.ms-excel', + 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'xlw' => 'application/vnd.ms-excel', + 'xml' => 'application/xml', + 'xo' => 'application/vnd.olpc-sugar', + 'xop' => 'application/xop+xml', + 'xpi' => 'application/x-xpinstall', + 'xpm' => 'image/x-xpixmap', + 'xpr' => 'application/vnd.is-xpr', + 'xps' => 'application/vnd.ms-xpsdocument', + 'xpw' => 'application/vnd.intercon.formnet', + 'xpx' => 'application/vnd.intercon.formnet', + 'xsl' => 'application/xml', + 'xslt' => 'application/xslt+xml', + 'xsm' => 'application/vnd.syncml+xml', + 'xspf' => 'application/xspf+xml', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xvm' => 'application/xv+xml', + 'xvml' => 'application/xv+xml', + 'xwd' => 'image/x-xwindowdump', + 'xyz' => 'chemical/x-xyz', + 'yaml' => 'text/yaml', + 'yang' => 'application/yang', + 'yin' => 'application/yin+xml', + 'yml' => 'text/yaml', + 'zaz' => 'application/vnd.zzazz.deck+xml', + 'zip' => 'application/zip', + 'zir' => 'application/vnd.zul', + 'zirz' => 'application/vnd.zul', + 'zmm' => 'application/vnd.handheld-entertainment+xml' + ); + + /** + * Get a singleton instance of the class + * + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Get a mimetype value from a file extension + * + * @param string $extension File extension + * + * @return string|null + * + */ + public function fromExtension($extension) + { + $extension = strtolower($extension); + + return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; + } + + /** + * Get a mimetype from a filename + * + * @param string $filename Filename to generate a mimetype from + * + * @return string|null + */ + public function fromFilename($filename) + { + return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php new file mode 100644 index 000000000..4b4e49d05 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php @@ -0,0 +1,20 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); + } else { + return array($key => implode(',', $value)); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php new file mode 100644 index 000000000..1bf1730e4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php @@ -0,0 +1,22 @@ +isUrlEncoding()) { + return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); + } else { + return array($key => $value); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php new file mode 100644 index 000000000..133ea2bd9 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php @@ -0,0 +1,27 @@ + $v) { + $k = "{$key}[{$k}]"; + if (is_array($v)) { + $ret = array_merge($ret, self::aggregate($k, $v, $query)); + } else { + $ret[$query->encodeValue($k)] = $query->encodeValue($v); + } + } + + return $ret; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php new file mode 100644 index 000000000..72bee620c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php @@ -0,0 +1,22 @@ +add($key, $value); + $foundDuplicates = true; + } elseif ($paramIsPhpStyleArray) { + $q[$key] = array($value); + } else { + $q[$key] = $value; + } + } else { + // Uses false by default to represent keys with no trailing "=" sign. + $q->add($key, false); + } + } + + // Use the duplicate aggregator if duplicates were found and not using PHP style arrays + if ($foundDuplicates && !$foundPhpStyle) { + $q->setAggregator(new DuplicateAggregator()); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + * @throws RuntimeException + */ + public function __toString() + { + if (!$this->data) { + return ''; + } + + $queryList = array(); + foreach ($this->prepareData($this->data) as $name => $value) { + $queryList[] = $this->convertKvp($name, $value); + } + + return implode($this->fieldSeparator, $queryList); + } + + /** + * Get the query string field separator + * + * @return string + */ + public function getFieldSeparator() + { + return $this->fieldSeparator; + } + + /** + * Get the query string value separator + * + * @return string + */ + public function getValueSeparator() + { + return $this->valueSeparator; + } + + /** + * Returns the type of URL encoding used by the query string + * + * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" + * + * @return bool|string + */ + public function getUrlEncoding() + { + return $this->urlEncode; + } + + /** + * Returns true or false if using URL encoding + * + * @return bool + */ + public function isUrlEncoding() + { + return $this->urlEncode !== false; + } + + /** + * Provide a function for combining multi-valued query string parameters into a single or multiple fields + * + * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting + * deeply nested query string variables into a flattened array. + * Pass null to use the default PHP style aggregator. For legacy + * reasons, this function accepts a callable that must accepts a + * $key, $value, and query object. + * @return self + * @see \Guzzle\Http\QueryString::aggregateUsingComma() + */ + public function setAggregator(QueryAggregatorInterface $aggregator = null) + { + // Use the default aggregator if none was set + if (!$aggregator) { + if (!self::$defaultAggregator) { + self::$defaultAggregator = new PhpAggregator(); + } + $aggregator = self::$defaultAggregator; + } + + $this->aggregator = $aggregator; + + return $this; + } + + /** + * Set whether or not field names and values should be rawurlencoded + * + * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or + * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) + * @return self + */ + public function useUrlEncoding($encode) + { + $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; + + return $this; + } + + /** + * Set the query string separator + * + * @param string $separator The query string separator that will separate fields + * + * @return self + */ + public function setFieldSeparator($separator) + { + $this->fieldSeparator = $separator; + + return $this; + } + + /** + * Set the query string value separator + * + * @param string $separator The query string separator that will separate values from fields + * + * @return self + */ + public function setValueSeparator($separator) + { + $this->valueSeparator = $separator; + + return $this; + } + + /** + * Returns an array of url encoded field names and values + * + * @return array + */ + public function urlEncode() + { + return $this->prepareData($this->data); + } + + /** + * URL encodes a value based on the url encoding type of the query string object + * + * @param string $value Value to encode + * + * @return string + */ + public function encodeValue($value) + { + if ($this->urlEncode == self::RFC_3986) { + return rawurlencode($value); + } elseif ($this->urlEncode == self::FORM_URLENCODED) { + return urlencode($value); + } else { + return (string) $value; + } + } + + /** + * Url encode parameter data and convert nested query strings into a flattened hash. + * + * @param array $data The data to encode + * + * @return array Returns an array of encoded values and keys + */ + protected function prepareData(array $data) + { + // If no aggregator is present then set the default + if (!$this->aggregator) { + $this->setAggregator(null); + } + + $temp = array(); + foreach ($data as $key => $value) { + if ($value === false || $value === null) { + // False and null will not include the "=". Use an empty string to include the "=". + $temp[$this->encodeValue($key)] = $value; + } elseif (is_array($value)) { + $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); + } else { + $temp[$this->encodeValue($key)] = $this->encodeValue($value); + } + } + + return $temp; + } + + /** + * Converts a key value pair that can contain strings, nulls, false, or arrays + * into a single string. + * + * @param string $name Name of the field + * @param mixed $value Value of the field + * @return string + */ + private function convertKvp($name, $value) + { + if ($value === self::BLANK || $value === null || $value === false) { + return $name; + } elseif (!is_array($value)) { + return $name . $this->valueSeparator . $value; + } + + $result = ''; + foreach ($value as $v) { + $result .= $this->convertKvp($name, $v) . $this->fieldSeparator; + } + + return rtrim($result, $this->fieldSeparator); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php new file mode 100644 index 000000000..a34893a66 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php @@ -0,0 +1,106 @@ +setLimit($limit)->setOffset($offset); + } + + /** + * Returns only a subset of the decorated entity body when cast as a string + * {@inheritdoc} + */ + public function __toString() + { + return substr((string) $this->body, $this->offset, $this->limit) ?: ''; + } + + public function isConsumed() + { + return $this->body->isConsumed() || + ($this->body->ftell() >= $this->offset + $this->limit); + } + + /** + * Returns the Content-Length of the limited subset of data + * {@inheritdoc} + */ + public function getContentLength() + { + $length = $this->body->getContentLength(); + + return $length === false + ? $this->limit + : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); + } + + /** + * Allow for a bounded seek on the read limited entity body + * {@inheritdoc} + */ + public function seek($offset, $whence = SEEK_SET) + { + return $whence === SEEK_SET + ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) + : false; + } + + /** + * Set the offset to start limiting from + * + * @param int $offset Offset to seek to and begin byte limiting from + * + * @return self + */ + public function setOffset($offset) + { + $this->body->seek($offset); + $this->offset = $offset; + + return $this; + } + + /** + * Set the limit of bytes that the decorator allows to be read from the stream + * + * @param int $limit Total number of bytes to allow to be read from the stream + * + * @return self + */ + public function setLimit($limit) + { + $this->limit = $limit; + + return $this; + } + + public function read($length) + { + // Check if the current position is less than the total allowed bytes + original offset + $remaining = ($this->offset + $this->limit) - $this->body->ftell(); + if ($remaining > 0) { + // Only return the amount of requested data, ensuring that the byte limit is not exceeded + return $this->body->read(min($remaining, $length)); + } else { + return false; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php new file mode 100644 index 000000000..1a824b8b7 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php @@ -0,0 +1,250 @@ + array('onRequestSent', 100), + 'request.clone' => 'cleanupRequest', + 'request.before_send' => 'cleanupRequest' + ); + } + + /** + * Clean up the parameters of a request when it is cloned + * + * @param Event $event Event emitted + */ + public function cleanupRequest(Event $event) + { + $params = $event['request']->getParams(); + unset($params[self::REDIRECT_COUNT]); + unset($params[self::PARENT_REQUEST]); + } + + /** + * Called when a request receives a redirect response + * + * @param Event $event Event emitted + */ + public function onRequestSent(Event $event) + { + $response = $event['response']; + $request = $event['request']; + + // Only act on redirect requests with Location headers + if (!$response || $request->getParams()->get(self::DISABLE)) { + return; + } + + // Trace the original request based on parameter history + $original = $this->getOriginalRequest($request); + + // Terminating condition to set the effective response on the original request + if (!$response->isRedirect() || !$response->hasHeader('Location')) { + if ($request !== $original) { + // This is a terminating redirect response, so set it on the original request + $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); + $original->setResponse($response); + $response->setEffectiveUrl($request->getUrl()); + } + return; + } + + $this->sendRedirectRequest($original, $request, $response); + } + + /** + * Get the original request that initiated a series of redirects + * + * @param RequestInterface $request Request to get the original request from + * + * @return RequestInterface + */ + protected function getOriginalRequest(RequestInterface $request) + { + $original = $request; + // The number of redirects is held on the original request, so determine which request that is + while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { + $original = $parent; + } + + return $original; + } + + /** + * Create a redirect request for a specific request object + * + * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do + * (e.g. redirect POST with GET). + * + * @param RequestInterface $request Request being redirected + * @param RequestInterface $original Original request + * @param int $statusCode Status code of the redirect + * @param string $location Location header of the redirect + * + * @return RequestInterface Returns a new redirect request + * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot + */ + protected function createRedirectRequest( + RequestInterface $request, + $statusCode, + $location, + RequestInterface $original + ) { + $redirectRequest = null; + $strict = $original->getParams()->get(self::STRICT_REDIRECTS); + + // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC + // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. + if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) { + $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); + } else { + $redirectRequest = clone $request; + } + + $redirectRequest->setIsRedirect(true); + // Always use the same response body when redirecting + $redirectRequest->setResponseBody($request->getResponseBody()); + + $location = Url::factory($location); + // If the location is not absolute, then combine it with the original URL + if (!$location->isAbsolute()) { + $originalUrl = $redirectRequest->getUrl(true); + // Remove query string parameters and just take what is present on the redirect Location header + $originalUrl->getQuery()->clear(); + $location = $originalUrl->combine((string) $location, true); + } + + $redirectRequest->setUrl($location); + + // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) + $redirectRequest->getEventDispatcher()->addListener( + 'request.before_send', + $func = function ($e) use (&$func, $request, $redirectRequest) { + $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); + $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); + } + ); + + // Rewind the entity body of the request if needed + if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { + $body = $redirectRequest->getBody(); + // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails + if ($body->ftell() && !$body->rewind()) { + throw new CouldNotRewindStreamException( + 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' + . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' + . 'entity body of the request using setRewindFunction().' + ); + } + } + + return $redirectRequest; + } + + /** + * Prepare the request for redirection and enforce the maximum number of allowed redirects per client + * + * @param RequestInterface $original Original request + * @param RequestInterface $request Request to prepare and validate + * @param Response $response The current response + * + * @return RequestInterface + */ + protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) + { + $params = $original->getParams(); + // This is a new redirect, so increment the redirect counter + $current = $params[self::REDIRECT_COUNT] + 1; + $params[self::REDIRECT_COUNT] = $current; + // Use a provided maximum value or default to a max redirect count of 5 + $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; + + // Throw an exception if the redirect count is exceeded + if ($current > $max) { + $this->throwTooManyRedirectsException($original, $max); + return false; + } else { + // Create a redirect request based on the redirect rules set on the request + return $this->createRedirectRequest( + $request, + $response->getStatusCode(), + trim($response->getLocation()), + $original + ); + } + } + + /** + * Send a redirect request and handle any errors + * + * @param RequestInterface $original The originating request + * @param RequestInterface $request The current request being redirected + * @param Response $response The response of the current request + * + * @throws BadResponseException|\Exception + */ + protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) + { + // Validate and create a redirect request based on the original request and current response + if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { + try { + $redirectRequest->send(); + } catch (BadResponseException $e) { + $e->getResponse(); + if (!$e->getResponse()) { + throw $e; + } + } + } + } + + /** + * Throw a too many redirects exception for a request + * + * @param RequestInterface $original Request + * @param int $max Max allowed redirects + * + * @throws TooManyRedirectsException when too many redirects have been issued + */ + protected function throwTooManyRedirectsException(RequestInterface $original, $max) + { + $original->getEventDispatcher()->addListener( + 'request.complete', + $func = function ($e) use (&$func, $original, $max) { + $original->getEventDispatcher()->removeListener('request.complete', $func); + $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); + throw new TooManyRedirectsException($str); + } + ); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem b/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem new file mode 100644 index 000000000..67f696abc --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem @@ -0,0 +1,3785 @@ +## +## ca-bundle.crt -- Bundle of CA Root Certificates +## +## Certificate data from Mozilla as of: Tue Jan 28 09:38:07 2014 +## +## This is a bundle of X.509 certificates of public Certificate Authorities +## (CA). These were automatically extracted from Mozilla's root certificates +## file (certdata.txt). This file can be found in the mozilla source tree: +## http://mxr.mozilla.org/mozilla-release/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 +## +## It contains the certificates in PEM format and therefore +## can be directly used with curl / libcurl / php_curl, or with +## an Apache+mod_ssl webserver for SSL client authentication. +## Just configure this file as the SSLCACertificateFile. +## + + +GTE CyberTrust Global Root +========================== +-----BEGIN CERTIFICATE----- +MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg +Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG +A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz +MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL +Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 +IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u +sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql +HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID +AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW +M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF +NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ +-----END CERTIFICATE----- + +Thawte Server CA +================ +-----BEGIN CERTIFICATE----- +MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE +AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j +b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV +BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u +c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG +A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 +ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl +/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 +1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J +GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ +GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= +-----END CERTIFICATE----- + +Thawte Premium Server CA +======================== +-----BEGIN CERTIFICATE----- +MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT +DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs +dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE +AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl +ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT +AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU +VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 +aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ +cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 +aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh +Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ +qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm +SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf +8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t +UCemDaYj+bvLpgcUQg== +-----END CERTIFICATE----- + +Equifax Secure CA +================= +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE +ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 +MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT +B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB +nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR +fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW +8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG +A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE +CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG +A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS +spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB +Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 +zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB +BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 +70+sB3c4 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA +TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah +WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf +Tqj/ZA1k +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G2 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT +MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz +dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO +FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 +lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB +MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT +1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD +Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 +-----END CERTIFICATE----- + +GlobalSign Root CA +================== +-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx +GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds +b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV +BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD +VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa +DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc +THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb +Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP +c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX +gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF +AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj +Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG +j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH +hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC +X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE----- + +GlobalSign Root CA - R2 +======================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 +ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp +s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN +S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL +TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C +ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E +FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i +YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN +BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp +9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu +01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 +9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE----- + +ValiCert Class 1 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy +MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi +GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm +DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG +lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX +icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP +Orf1LXLI +-----END CERTIFICATE----- + +ValiCert Class 2 VA +=================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC +CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf +ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ +SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV +UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 +W9ViH0Pd +-----END CERTIFICATE----- + +RSA Root Certificate 1 +====================== +-----BEGIN CERTIFICATE----- +MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp +b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs +YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh +bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw +MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 +d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg +UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 +LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td +3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H +BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs +3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF +V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r +on+jjBXu +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 +EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc +cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw +EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj +055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f +j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC +/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 +xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa +t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== +-----END CERTIFICATE----- + +Verisign Class 4 Public Primary Certification Authority - G3 +============================================================ +-----BEGIN CERTIFICATE----- +MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw +CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy +dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS +tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM +8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW +Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX +Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA +j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt +mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm +fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd +RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG +UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== +-----END CERTIFICATE----- + +Entrust.net Secure Server CA +============================ +-----BEGIN CERTIFICATE----- +MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV +BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg +cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl +ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv +cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG +A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi +eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p +dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ +aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 +gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw +ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw +CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l +dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF +bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu +dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw +NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow +HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA +BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN +Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 +n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= +-----END CERTIFICATE----- + +Entrust.net Premium 2048 Secure Server CA +========================================= +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u +ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp +bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV +BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx +NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 +d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl +MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u +ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL +Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr +hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW +nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi +VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ +KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy +T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf +zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT +J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e +nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= +-----END CERTIFICATE----- + +Baltimore CyberTrust Root +========================= +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE +ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li +ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC +SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs +dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME +uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB +UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C +G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 +XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr +l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI +VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB +BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh +cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 +hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa +Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H +RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- + +Equifax Secure Global eBusiness CA +================================== +-----BEGIN CERTIFICATE----- +MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp +bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx +HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds +b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV +PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN +qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn +hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j +BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs +MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN +I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY +NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV +-----END CERTIFICATE----- + +Equifax Secure eBusiness CA 1 +============================= +-----BEGIN CERTIFICATE----- +MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB +LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE +ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz +IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ +1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a +IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk +MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW +Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF +AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 +lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ +KpYrtWKmpj29f5JZzVoqgrI3eQ== +-----END CERTIFICATE----- + +AddTrust Low-Value Services Root +================================ +-----BEGIN CERTIFICATE----- +MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU +cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw +CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO +ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 +54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr +oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 +Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui +GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w +HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD +AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT +RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw +HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt +ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph +iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY +eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr +mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj +ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= +-----END CERTIFICATE----- + +AddTrust External Root +====================== +-----BEGIN CERTIFICATE----- +MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD +VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw +NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU +cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg +Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 ++iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw +Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo +aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy +2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 +7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL +VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk +VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl +j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 +6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 +e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u +G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= +-----END CERTIFICATE----- + +AddTrust Public Services Root +============================= +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU +cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ +BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l +dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu +nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i +d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG +Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw +HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G +A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux +FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G +A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 +JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL ++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao +GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 +Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H +EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= +-----END CERTIFICATE----- + +AddTrust Qualified Certificates Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML +QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU +cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx +CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ +IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx +64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 +KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o +L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR +wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU +MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE +BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y +azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD +ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG +GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X +dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze +RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB +iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= +-----END CERTIFICATE----- + +Entrust Root Certification Authority +==================================== +-----BEGIN CERTIFICATE----- +MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV +BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw +b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG +A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 +MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu +MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu +Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v +dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz +A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww +Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 +j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN +rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 +MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH +hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA +A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM +Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa +v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS +W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 +tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 +-----END CERTIFICATE----- + +RSA Security 2048 v3 +==================== +-----BEGIN CERTIFICATE----- +MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK +ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy +MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb +BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 +Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb +WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH +KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP ++Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ +MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E +FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY +v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj +0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj +VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 +nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA +pKnXwiJPZ9d37CAFYd4= +-----END CERTIFICATE----- + +GeoTrust Global CA +================== +-----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK +Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw +MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo +BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet +8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc +T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU +vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD +AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk +DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q +zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 +d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 +mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p +XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm +Mw== +-----END CERTIFICATE----- + +GeoTrust Global CA 2 +==================== +-----BEGIN CERTIFICATE----- +MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw +MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j +LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ +NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k +LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA +Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b +HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH +K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 +srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh +ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL +OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC +x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF +H4z1Ir+rzoPz4iIprn2DQKi6bA== +-----END CERTIFICATE----- + +GeoTrust Universal CA +===================== +-----BEGIN CERTIFICATE----- +MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 +MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu +Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP +ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t +JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e +RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs +7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d +8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V +qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga +Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB +Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu +KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 +ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 +XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB +hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc +aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 +qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL +oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK +xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF +KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 +DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK +xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU +p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI +P/rmMuGNG2+k5o7Y+SlIis5z/iw= +-----END CERTIFICATE----- + +GeoTrust Universal CA 2 +======================= +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN +R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 +MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg +SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 +DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 +j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q +JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a +QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 +WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP +20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn +ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC +SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG +8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 ++/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E +BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z +dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ +4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ +mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq +A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg +Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP +pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d +FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp +gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm +X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS +-----END CERTIFICATE----- + +America Online Root Certification Authority 1 +============================================= +-----BEGIN CERTIFICATE----- +MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG +v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z +DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh +sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP +8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T +AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z +o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf +GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF +VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft +3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g +Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds +sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 +-----END CERTIFICATE----- + +America Online Root Certification Authority 2 +============================================= +-----BEGIN CERTIFICATE----- +MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT +QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp +Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG +A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg +T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en +fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 +f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO +qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN +RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 +gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn +6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid +FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 +Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj +B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op +aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE +AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY +T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p ++DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg +JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy +zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO +ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh +1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf +GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff +Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP +cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= +-----END CERTIFICATE----- + +Visa eCommerce Root +=================== +-----BEGIN CERTIFICATE----- +MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG +EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug +QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 +WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm +VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv +bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL +F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b +RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 +TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI +/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs +GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG +MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc +CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW +YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz +zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu +YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt +398znM/jra6O1I7mT1GvFpLgXPYHDw== +-----END CERTIFICATE----- + +Certum Root CA +============== +-----BEGIN CERTIFICATE----- +MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK +ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla +Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u +by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x +wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL +kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ +89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K +Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P +NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ +GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg +GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ +0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS +qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== +-----END CERTIFICATE----- + +Comodo AAA Services root +======================== +-----BEGIN CERTIFICATE----- +MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw +MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl +c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV +BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG +C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs +i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW +Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH +Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK +Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f +BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl +cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz +LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm +7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz +Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z +8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C +12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== +-----END CERTIFICATE----- + +Comodo Secure Services root +=========================== +-----BEGIN CERTIFICATE----- +MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw +MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu +Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi +BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP +9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc +rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC +oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V +p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E +FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w +gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj +YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm +aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm +4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj +Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL +DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw +pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H +RR3B7Hzs/Sk= +-----END CERTIFICATE----- + +Comodo Trusted Services root +============================ +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS +R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg +TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw +MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h +bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw +IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 +3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y +/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 +juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS +ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud +DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB +/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp +ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl +cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw +uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 +pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA +BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l +R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O +9y5Xt5hwXsjEeLBi +-----END CERTIFICATE----- + +QuoVadis Root CA +================ +-----BEGIN CERTIFICATE----- +MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE +ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 +eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz +MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp +cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD +EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk +J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL +F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL +YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen +AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w +PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y +ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 +MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj +YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs +ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh +Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW +Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu +BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw +FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 +tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo +fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul +LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x +gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi +5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi +5nrQNiOKSnQ2+Q== +-----END CERTIFICATE----- + +QuoVadis Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +QuoVadis Root CA 3 +================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Security Communication Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP +U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw +8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM +DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX +5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd +DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 +JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw +DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g +0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a +mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ +s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ +6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi +FL39vmwLAw== +-----END CERTIFICATE----- + +Sonera Class 2 Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG +U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw +NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh +IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 +/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT +dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG +f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P +tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH +nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT +XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt +0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI +cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph +Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx +EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH +llpwrN9M +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA +============================= +-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE +ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g +Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w +HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh +bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt +vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P +jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca +C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth +vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 +22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV +HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v +dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN +BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR +EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw +MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y +nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR +iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== +-----END CERTIFICATE----- + +TDC Internet Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE +ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx +NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu +ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j +xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL +znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc +5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 +otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI +AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM +VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM +MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC +AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe +UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G +CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m +gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ +2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb +O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU +Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l +-----END CERTIFICATE----- + +UTN DATACorp SGC Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ +BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa +MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w +HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy +dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys +raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo +wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA +9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv +33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud +DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 +BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD +LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 +DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 +I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx +EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP +DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI +-----END CERTIFICATE----- + +UTN USERFirst Hardware Root CA +============================== +-----BEGIN CERTIFICATE----- +MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE +BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl +IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd +BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx +OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 +eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz +ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI +wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd +tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 +i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf +Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw +gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF +lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF +UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF +BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM +//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW +XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 +lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn +iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 +nfhmqA== +-----END CERTIFICATE----- + +Camerfirma Chambers of Commerce Root +==================================== +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx +NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp +cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn +MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC +AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU +xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH +NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW +DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV +d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud +EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v +cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P +AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh +bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD +VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz +aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi +fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD +L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN +UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n +ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 +erfutGWaIZDgqtCYvDi1czyL+Nw= +-----END CERTIFICATE----- + +Camerfirma Global Chambersign Root +================================== +-----BEGIN CERTIFICATE----- +MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe +QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i +ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx +NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt +YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg +MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw +ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J +1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O +by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl +6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c +8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ +BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j +aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B +Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj +aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y +ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh +bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA +PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y +gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ +PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 +IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes +t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== +-----END CERTIFICATE----- + +NetLock Notary (Class A) Root +============================= +-----BEGIN CERTIFICATE----- +MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI +EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j +ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX +DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH +EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD +VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz +cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM +D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ +z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC +/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 +tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 +4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG +A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC +Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv +bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu +IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn +LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 +ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz +IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh +IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu +b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh +bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg +Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp +bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 +ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP +ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB +CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr +KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM +8CgHrTwXZoi1/baI +-----END CERTIFICATE----- + +NetLock Business (Class B) Root +=============================== +-----BEGIN CERTIFICATE----- +MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg +VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD +VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv +bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg +VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S +o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr +1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV +HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ +RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh +dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 +ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv +c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg +YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh +c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz +Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA +bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl +IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 +YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj +cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM +43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR +stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI +-----END CERTIFICATE----- + +NetLock Express (Class C) Root +============================== +-----BEGIN CERTIFICATE----- +MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT +CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV +BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD +KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ +BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 +dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j +ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z +W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 +euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw +DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN +RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn +YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB +IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i +aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 +ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs +ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo +dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y +emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k +IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ +UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg +YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 +xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW +gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== +-----END CERTIFICATE----- + +XRamp Global CA Root +==================== +-----BEGIN CERTIFICATE----- +MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE +BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj +dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx +HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg +U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu +IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx +foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE +zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs +AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry +xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap +oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC +AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc +/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt +qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n +nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz +8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= +-----END CERTIFICATE----- + +Go Daddy Class 2 CA +=================== +-----BEGIN CERTIFICATE----- +MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY +VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG +A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g +RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD +ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv +2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 +qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j +YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY +vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O +BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o +atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu +MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim +PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt +I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ +HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI +Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b +vZ8= +-----END CERTIFICATE----- + +Starfield Class 2 CA +==================== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc +U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg +Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo +MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG +A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG +SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY +bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ +JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm +epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN +F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF +MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f +hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo +bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g +QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs +afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM +PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl +xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD +KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 +QBFGmh95DmK/D5fs4C8fF5Q= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE +FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 +Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj +YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH +AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw +Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg +U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 +LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl +cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh +cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT +dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC +AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh +3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm +vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk +fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 +fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ +EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq +yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl +1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ +lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro +g14= +-----END CERTIFICATE----- + +Taiwan GRCA +=========== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG +EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X +DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv +dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN +w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 +BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O +1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO +htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov +J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 +Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t +B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB +O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 +lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV +HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 +09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ +TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj +Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 +Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU +D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz +DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk +Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk +7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ +CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy ++fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS +-----END CERTIFICATE----- + +Firmaprofesional Root CA +======================== +-----BEGIN CERTIFICATE----- +MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT +GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp +Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA +ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL +MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT +OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 +ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V +j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH +lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf +3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 +NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww +KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG +AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud +DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD +ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq +u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf +wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm +7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG +VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= +-----END CERTIFICATE----- + +Swisscom Root CA 1 +================== +-----BEGIN CERTIFICATE----- +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 +NY6E0F/6MBr1mmz0DlP5OlvRHA== +-----END CERTIFICATE----- + +DigiCert Assured ID Root CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw +IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx +MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO +9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy +UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW +/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy +oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf +GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF +66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq +hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc +EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn +SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i +8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe ++o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== +-----END CERTIFICATE----- + +DigiCert Global Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw +HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw +MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 +dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq +hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn +TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 +BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H +4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y +7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB +o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm +8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF +BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr +EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt +tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 +UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk +CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= +-----END CERTIFICATE----- + +DigiCert High Assurance EV Root CA +================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG +EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw +KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw +MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ +MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu +Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t +Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS +OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 +MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ +NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe +h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB +Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY +JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ +V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp +myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK +mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K +-----END CERTIFICATE----- + +Certplus Class 2 Primary CA +=========================== +-----BEGIN CERTIFICATE----- +MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE +BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN +OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy +dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR +5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ +Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO +YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e +e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME +CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ +YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t +L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD +P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R +TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ +7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW +//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 +l7+ijrRU +-----END CERTIFICATE----- + +DST Root CA X3 +============== +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK +ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X +DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 +cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT +rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 +UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy +xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d +utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ +MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug +dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE +GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw +RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS +fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- + +DST ACES CA X6 +============== +-----BEGIN CERTIFICATE----- +MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT +MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha +MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE +CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI +DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa +pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow +GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy +MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud +EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu +Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy +dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU +CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 +5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t +Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq +nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs +vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 +oKfN5XozNmr6mis= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 1 +============================================== +-----BEGIN CERTIFICATE----- +MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP +MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 +acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx +MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg +U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB +TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC +aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX +yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i +Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ +8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 +W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME +BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 +sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE +q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy +B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY +nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2 +============================================== +-----BEGIN CERTIFICATE----- +MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN +MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr +dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe +LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI +x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g +QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr +5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB +AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G +A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt +Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 +Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ +hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P +9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 +UrbnBEI= +-----END CERTIFICATE----- + +SwissSign Gold CA - G2 +====================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +SwissSign Silver CA - G2 +======================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority +======================================== +-----BEGIN CERTIFICATE----- +MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ +cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN +b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 +nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge +RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt +tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD +AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI +hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K +Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN +NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa +Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG +1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= +-----END CERTIFICATE----- + +thawte Primary Root CA +====================== +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 +MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg +SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv +KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT +FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs +oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ +1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc +q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K +aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p +afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF +AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE +uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 +jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH +z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G5 +============================================================ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp +ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln +biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh +dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz +j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD +Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ +Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r +fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv +Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG +SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ +X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE +KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC +Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE +ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE----- + +SecureTrust CA +============== +-----BEGIN CERTIFICATE----- +MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy +dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe +BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX +OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t +DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH +GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b +01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH +ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj +aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu +SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf +mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ +nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR +3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= +-----END CERTIFICATE----- + +Secure Global CA +================ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG +EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH +bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg +MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg +Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx +YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ +bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g +8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV +HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi +0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud +EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn +oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA +MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ +OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn +CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 +3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc +f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW +-----END CERTIFICATE----- + +COMODO Certification Authority +============================== +-----BEGIN CERTIFICATE----- +MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE +BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG +A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb +MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD +T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH ++7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww +xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV +4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA +1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI +rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k +b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC +AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP +OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ +RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc +IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN ++8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== +-----END CERTIFICATE----- + +Network Solutions Certificate Authority +======================================= +-----BEGIN CERTIFICATE----- +MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG +EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr +IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx +MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu +MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx +jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT +aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT +crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc +/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB +AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv +bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA +A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q +4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ +GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv +wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD +ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey +-----END CERTIFICATE----- + +WellsSecure Public Root Certificate Authority +============================================= +-----BEGIN CERTIFICATE----- +MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM +F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw +NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN +MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl +bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD +VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 +iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 +i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 +bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB +K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB +AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu +cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm +lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB +i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww +GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI +K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 +bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj +qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es +E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ +tylv2G0xffX8oRAHh84vWdw+WNs= +-----END CERTIFICATE----- + +COMODO ECC Certification Authority +================================== +-----BEGIN CERTIFICATE----- +MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC +R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE +ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB +dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix +GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X +4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni +wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E +BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG +FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA +U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= +-----END CERTIFICATE----- + +IGC/A +===== +-----BEGIN CERTIFICATE----- +MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD +VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE +Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy +MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI +EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT +STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 +TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW +So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy +HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd +frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ +tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB +egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC +iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK +q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q +MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg +Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI +lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF +0mBWWg== +-----END CERTIFICATE----- + +Security Communication EV RootCA1 +================================= +-----BEGIN CERTIFICATE----- +MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE +BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl +Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO +/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX +WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z +ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 +bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK +9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG +SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm +iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG +Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW +mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW +T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 +-----END CERTIFICATE----- + +OISTE WISeKey Global Root GA CA +=============================== +-----BEGIN CERTIFICATE----- +MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE +BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG +A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH +bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD +VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw +IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 +IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 +Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg +Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD +d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ +/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R +LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw +AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ +KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm +MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 ++vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa +hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY +okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA +========================= +-----BEGIN CERTIFICATE----- +MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE +BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL +EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 +MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz +dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT +GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG +d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N +oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc +QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ +PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb +MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG +IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD +VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 +LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A +dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn +AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA +4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg +AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA +egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 +Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO +PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv +c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h +cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw +IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT +WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV +MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER +MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp +Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal +HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT +nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE +aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a +86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK +yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB +S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= +-----END CERTIFICATE----- + +Certigna +======== +-----BEGIN CERTIFICATE----- +MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw +EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 +MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI +Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q +XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH +GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p +ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg +DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf +Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ +tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ +BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J +SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA +hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ +ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu +PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY +1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw +WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== +-----END CERTIFICATE----- + +AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. +====================================== +-----BEGIN CERTIFICATE----- +MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT +AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg +LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w +HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ +U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh +IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN +yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU +2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 +4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP +2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm +8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf +HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa +Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK +5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b +czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE +AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g +ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF +BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug +cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf +AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX +EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v +/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 +MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 +3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk +eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f +/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h +RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU +Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 2 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw +MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw +IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 +xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ +Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u +SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G +dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ +KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj +TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP +JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk +vQ== +-----END CERTIFICATE----- + +TC TrustCenter Class 3 CA II +============================ +-----BEGIN CERTIFICATE----- +MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy +IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw +MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 +c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE +AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W +yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo +6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ +uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk +2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB +7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 +Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU +cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i +SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u +TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE +O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 +yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 +IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal +092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc +5A== +-----END CERTIFICATE----- + +TC TrustCenter Universal CA I +============================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC +REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy +IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN +MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg +VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw +JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC +qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv +xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw +ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O +gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j +BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG +1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy +vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 +ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT +ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a +7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY +-----END CERTIFICATE----- + +Deutsche Telekom Root CA 2 +========================== +-----BEGIN CERTIFICATE----- +MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT +RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG +A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 +MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G +A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS +b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 +bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI +KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY +AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK +Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV +jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV +HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr +E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy +zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 +rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G +dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU +Cm26OWMohpLzGITY+9HPBVZkVw== +-----END CERTIFICATE----- + +ComSign Secured CA +================== +-----BEGIN CERTIFICATE----- +MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE +AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w +NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD +QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs +49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH +7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB +kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 +9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw +AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t +U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA +j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC +AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a +BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp +FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP +51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz +OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== +-----END CERTIFICATE----- + +Cybertrust Global Root +====================== +-----BEGIN CERTIFICATE----- +MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li +ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 +MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD +ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA ++Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW +0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL +AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin +89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT +8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 +MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G +A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO +lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi +5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 +hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T +X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW +WL1WMRJOEcgh4LMRkWXbtKaIOM5V +-----END CERTIFICATE----- + +ePKI Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG +EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg +Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx +MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq +MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B +AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs +IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi +lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv +qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX +12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O +WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ +ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao +lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ +vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi +Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi +MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH +ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 +1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq +KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV +xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP +NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r +GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE +xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx +gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy +sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD +BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= +-----END CERTIFICATE----- + +T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 +============================================================================================================================= +-----BEGIN CERTIFICATE----- +MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH +DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q +aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry +b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV +BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg +S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 +MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl +IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF +n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl +IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft +dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl +cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO +Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 +xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR +6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL +hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd +BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 +N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT +y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh +LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M +dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= +-----END CERTIFICATE----- + +Buypass Class 2 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 +MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M +cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 +0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 +0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R +uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV +1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt +7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 +fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w +wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho +-----END CERTIFICATE----- + +Buypass Class 3 CA 1 +==================== +-----BEGIN CERTIFICATE----- +MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 +MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh +c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx +ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 +n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia +AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c +1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P +AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 +pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA +EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 +htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj +el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 +-----END CERTIFICATE----- + +EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 +========================================================================== +-----BEGIN CERTIFICATE----- +MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg +QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe +Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p +ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt +IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by +X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b +gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr +eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ +TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy +Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn +uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI +qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm +ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0 +Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW +Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t +FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm +zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k +XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT +bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU +RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK +1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt +2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ +Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9 +AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT +-----END CERTIFICATE----- + +certSIGN ROOT CA +================ +-----BEGIN CERTIFICATE----- +MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD +VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa +Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE +CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I +JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH +rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 +ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD +0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 +AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B +Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB +AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 +SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 +x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt +vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz +TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD +-----END CERTIFICATE----- + +CNNIC ROOT +========== +-----BEGIN CERTIFICATE----- +MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE +ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw +OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD +o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz +VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT +VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or +czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK +y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC +wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S +lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 +Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM +O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 +BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 +G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m +mxE= +-----END CERTIFICATE----- + +ApplicationCA - Japanese Government +=================================== +-----BEGIN CERTIFICATE----- +MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT +SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw +MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl +cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 +fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN +wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE +jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu +nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU +WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV +BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD +vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs +o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g +/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD +io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW +dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL +rosot4LKGAfmt1t06SAZf7IbiVQ= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G3 +============================================= +-----BEGIN CERTIFICATE----- +MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE +BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 +IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy +eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz +NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo +YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT +LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j +K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE +c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C +IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu +dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr +2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 +cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE +Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD +AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s +t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt +-----END CERTIFICATE----- + +thawte Primary Root CA - G2 +=========================== +-----BEGIN CERTIFICATE----- +MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC +VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu +IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg +Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV +MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG +b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt +IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS +LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 +8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU +mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN +G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K +rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== +-----END CERTIFICATE----- + +thawte Primary Root CA - G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE +BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 +aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv +cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w +ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh +d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD +VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG +A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At +P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC ++BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY +7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW +vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ +KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK +A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu +t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC +8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm +er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= +-----END CERTIFICATE----- + +GeoTrust Primary Certification Authority - G2 +============================================= +-----BEGIN CERTIFICATE----- +MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC +VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu +Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD +ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 +OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg +MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl +b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG +BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc +KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD +VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ +EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m +ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 +npaqBA+K +-----END CERTIFICATE----- + +VeriSign Universal Root Certification Authority +=============================================== +-----BEGIN CERTIFICATE----- +MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk +IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u +IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV +UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv +cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj +1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP +MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 +9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I +AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR +tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G +CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O +a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud +DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 +Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx +Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx +P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P +wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 +mJO37M2CYfE45k+XmCpajQ== +-----END CERTIFICATE----- + +VeriSign Class 3 Public Primary Certification Authority - G4 +============================================================ +-----BEGIN CERTIFICATE----- +MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC +VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 +b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz +ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj +YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU +cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo +b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 +Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz +rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB +/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw +HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u +Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD +A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx +AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== +-----END CERTIFICATE----- + +NetLock Arany (Class Gold) Főtanúsítvány +============================================ +-----BEGIN CERTIFICATE----- +MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G +A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 +dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB +cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx +MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO +ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv +biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 +c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu +0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw +/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk +H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw +fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 +neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB +BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW +qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta +YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC +bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna +NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu +dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= +-----END CERTIFICATE----- + +Staat der Nederlanden Root CA - G2 +================================== +-----BEGIN CERTIFICATE----- +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 +66+KAQ== +-----END CERTIFICATE----- + +CA Disig +======== +-----BEGIN CERTIFICATE----- +MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK +QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw +MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz +bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm +GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD +Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo +hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt +ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w +gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P +AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz +aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff +ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa +BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t +WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 +mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ +CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K +ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA +4Z7CRneC9VkGjCFMhwnN5ag= +-----END CERTIFICATE----- + +Juur-SK +======= +-----BEGIN CERTIFICATE----- +MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA +c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw +DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG +SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy +aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf +TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC ++Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw +UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa +Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF +MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD +HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh +AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA +cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr +AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw +cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE +FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G +A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo +ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL +abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 +IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh +Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 +yyqcjg== +-----END CERTIFICATE----- + +Hongkong Post Root CA 1 +======================= +-----BEGIN CERTIFICATE----- +MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT +DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx +NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n +IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 +ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr +auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh +qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY +V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV +HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i +h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio +l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei +IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps +T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT +c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== +-----END CERTIFICATE----- + +SecureSign RootCA11 +=================== +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi +SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS +b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw +KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 +cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL +TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO +wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq +g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP +O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA +bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX +t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh +OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r +bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ +Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 +y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 +lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= +-----END CERTIFICATE----- + +ACEDICOM Root +============= +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Verisign Class 3 Public Primary Certification Authority +======================================================= +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx +FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 +IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow +XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz +IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA +A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 +f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol +hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky +CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX +bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ +D/xwzoiQ +-----END CERTIFICATE----- + +Microsec e-Szigno Root CA 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER +MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv +c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o +dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE +BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt +U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA +fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG +0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA +pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm +1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC +AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf +QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE +FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o +lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX +I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 +tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 +yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi +LXpUq3DDfSJlgnCW +-----END CERTIFICATE----- + +E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi +=================================================== +-----BEGIN CERTIFICATE----- +MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz +ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 +MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 +cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u +aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY +8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y +jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI +JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk +9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD +AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG +SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d +F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq +D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 +Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq +fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX +-----END CERTIFICATE----- + +GlobalSign Root CA - R3 +======================= +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv +YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh +bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT +aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln +bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt +iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ +0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 +rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl +OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 +xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE +FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 +lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 +EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E +bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 +YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r +kpeDMdmztcpHWD9f +-----END CERTIFICATE----- + +Autoridad de Certificacion Firmaprofesional CIF A62634068 +========================================================= +-----BEGIN CERTIFICATE----- +MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA +BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 +MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw +QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB +NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD +Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P +B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY +7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH +ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI +plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX +MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX +LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK +bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU +vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud +EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH +DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp +cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA +bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx +ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx +51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk +R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP +T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f +Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl +osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR +crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR +saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD +KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi +6Et8Vcad+qMUu2WFbm5PEn4KPJ2V +-----END CERTIFICATE----- + +Izenpe.com +========== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Chambers of Commerce Root - 2008 +================================ +-----BEGIN CERTIFICATE----- +MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy +Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl +ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF +EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl +cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA +XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj +h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ +ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk +NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g +D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 +lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ +0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj +ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 +EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI +G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ +BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh +bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh +bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC +CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH +AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 +wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH +3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU +RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 +M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 +YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF +9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK +zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG +nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg +OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ +-----END CERTIFICATE----- + +Global Chambersign Root - 2008 +============================== +-----BEGIN CERTIFICATE----- +MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD +MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv +bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu +QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx +NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg +Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ +QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD +aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf +VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf +XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 +ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB +/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA +TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M +H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe +Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF +HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh +wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB +AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT +BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE +BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm +aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm +aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp +1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 +dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG +/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 +ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s +dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg +9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH +foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du +qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr +P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq +c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z +09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B +-----END CERTIFICATE----- + +Go Daddy Root Certificate Authority - G2 +======================================== +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu +MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 +MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 +b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G +A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq +9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD ++qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd +fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl +NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC +MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 +BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac +vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r +5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV +N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO +LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 +-----END CERTIFICATE----- + +Starfield Root Certificate Authority - G2 +========================================= +-----BEGIN CERTIFICATE----- +MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 +eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw +DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg +VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB +dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv +W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs +bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk +N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf +ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU +JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol +TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx +4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw +F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K +pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ +c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 +-----END CERTIFICATE----- + +Starfield Services Root Certificate Authority - G2 +================================================== +-----BEGIN CERTIFICATE----- +MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT +B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s +b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl +IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV +BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT +dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg +Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 +h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa +hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP +LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB +rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG +SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP +E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy +xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd +iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza +YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 +-----END CERTIFICATE----- + +AffirmTrust Commercial +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw +MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb +DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV +C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 +BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww +MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV +HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG +hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi +qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv +0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh +sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= +-----END CERTIFICATE----- + +AffirmTrust Networking +====================== +-----BEGIN CERTIFICATE----- +MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw +MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly +bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE +Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI +dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 +/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb +h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV +HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu +UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 +12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 +WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 +/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= +-----END CERTIFICATE----- + +AffirmTrust Premium +=================== +-----BEGIN CERTIFICATE----- +MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS +BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy +OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy +dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn +BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV +5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs ++7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd +GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R +p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI +S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 +6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 +/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo ++Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB +/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv +MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg +Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC +6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S +L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK ++4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV +BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg +IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 +g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb +zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== +-----END CERTIFICATE----- + +AffirmTrust Premium ECC +======================= +-----BEGIN CERTIFICATE----- +MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV +BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx +MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U +cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ +N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW +BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK +BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X +57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM +eQ== +-----END CERTIFICATE----- + +Certum Trusted Network CA +========================= +-----BEGIN CERTIFICATE----- +MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK +ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv +biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy +MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU +ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC +l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J +J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 +fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 +cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB +Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj +jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 +mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj +Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI +03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= +-----END CERTIFICATE----- + +Certinomis - Autorité Racine +============================= +-----BEGIN CERTIFICATE----- +MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg +LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG +A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw +JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa +wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly +Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw +2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N +jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q +c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC +lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb +xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g +530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna +4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ +KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x +WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva +R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 +nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B +CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv +JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE +qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b +WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE +wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ +vgt2Fl43N+bYdJeimUV5 +-----END CERTIFICATE----- + +Root CA Generalitat Valenciana +============================== +-----BEGIN CERTIFICATE----- +MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE +ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 +IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 +WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE +CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 +F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B +ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ +D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte +JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB +AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n +dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB +ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl +AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA +YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy +AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA +aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt +AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA +YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu +AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA +OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 +dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV +BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G +A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S +b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh +TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz +Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 +NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH +iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt ++GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= +-----END CERTIFICATE----- + +A-Trust-nQual-03 +================ +-----BEGIN CERTIFICATE----- +MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE +Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy +a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R +dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw +RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 +ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 +c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA +zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n +yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE +SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 +iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V +cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV +eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 +ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr +sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd +JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS +mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 +ahq97BvIxYSazQ== +-----END CERTIFICATE----- + +TWCA Root Certification Authority +================================= +-----BEGIN CERTIFICATE----- +MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ +VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG +EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB +IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx +QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC +oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP +4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r +y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG +9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC +mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW +QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY +T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny +Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== +-----END CERTIFICATE----- + +Security Communication RootCA2 +============================== +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc +U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh +dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC +SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy +aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ ++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R +3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV +spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K +EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 +QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB +CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj +u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk +3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q +tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 +mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 +-----END CERTIFICATE----- + +EC-ACC +====== +-----BEGIN CERTIFICATE----- +MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE +BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w +ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD +VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE +CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT +BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 +MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt +SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl +Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh +cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK +w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT +ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 +HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a +E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw +0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD +VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 +Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l +dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ +lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa +Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe +l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 +E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D +5EI= +-----END CERTIFICATE----- + +Hellenic Academic and Research Institutions RootCA 2011 +======================================================= +-----BEGIN CERTIFICATE----- +MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT +O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y +aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT +AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z +IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo +IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI +1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa +71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u +8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH +3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ +MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 +MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu +b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt +XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 +TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD +/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N +7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 +-----END CERTIFICATE----- + +Actalis Authentication Root CA +============================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- + +Trustis FPS Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 +IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV +BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ +RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk +H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa +cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt +o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA +AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd +BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c +GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC +yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P +8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV +l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl +iB6XzCGcKQENZetX2fNXlrtIzYE= +-----END CERTIFICATE----- + +StartCom Certification Authority +================================ +-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu +ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 +NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk +LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg +U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y +o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ +Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d +eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt +2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z +6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ +osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ +untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc +UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT +37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ +Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 +dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu +c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv +bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 +aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t +L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG +cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 +fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm +N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN +Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T +tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX +e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA +2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs +HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib +D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= +-----END CERTIFICATE----- + +StartCom Certification Authority G2 +=================================== +-----BEGIN CERTIFICATE----- +MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN +U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg +RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE +ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp +dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O +o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG +4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi +Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul +Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs +O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H +vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L +nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS +FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa +z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E +BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ +KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K +2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk +J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ +JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG +/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc +nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld +blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc +l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm +7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm +obp573PYtlNXLfbQ4ddI +-----END CERTIFICATE----- + +Buypass Class 2 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X +DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 +g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn +9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b +/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU +CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff +awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI +zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn +Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX +Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs +M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s +A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI +osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S +aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd +DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD +LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 +oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC +wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS +CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN +rJgWVqA= +-----END CERTIFICATE----- + +Buypass Class 3 Root CA +======================= +-----BEGIN CERTIFICATE----- +MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU +QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X +DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 +eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH +sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR +5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh +7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ +ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH +2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV +/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ +RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA +Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq +j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD +VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF +AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV +cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G +uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG +Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 +ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 +KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz +6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug +UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe +eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi +Cp/HuZc= +-----END CERTIFICATE----- + +T-TeleSec GlobalRoot Class 3 +============================ +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM +IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU +cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx +MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz +dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD +ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK +9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU +NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF +iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W +0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr +AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb +fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT +ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h +P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml +e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== +-----END CERTIFICATE----- + +EE Certification Centre Root CA +=============================== +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG +EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy +dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw +MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB +UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy +ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM +TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 +rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw +93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN +P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T +AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ +MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF +BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj +xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM +lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u +uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU +3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM +dcGWxZ0= +-----END CERTIFICATE----- + +TURKTRUST Certificate Services Provider Root 2007 +================================================= +-----BEGIN CERTIFICATE----- +MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP +MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg +QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X +DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl +a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N +YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv +KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya +KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT +rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC +AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s +Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I +aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO +Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb +BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK +poRq0Tl9 +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 2009 +============================== +-----BEGIN CERTIFICATE----- +MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe +Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE +LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD +ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA +BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv +KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z +p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC +AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ +4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y +eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw +MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G +PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw +OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm +2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 +o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV +dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph +X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= +-----END CERTIFICATE----- + +D-TRUST Root Class 3 CA 2 EV 2009 +================================= +-----BEGIN CERTIFICATE----- +MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK +DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw +OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS +egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh +zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T +7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 +sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 +11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv +cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v +ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El +MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp +b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh +c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ +PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 +nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX +ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA +NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv +w9y4AyHqnxbxLFS1 +-----END CERTIFICATE----- + +PSCProcert +========== +-----BEGIN CERTIFICATE----- +MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk +ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ +MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz +dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl +cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw +IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw +MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w +DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD +ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp +Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC +wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA +3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh +RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO +EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 +0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH +0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU +td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw +Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp +r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ +AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz +Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId +xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp +ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH +EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h +Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k +ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG +9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG +MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG +LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 +ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy +YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v +Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o +dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq +T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN +g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q +uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 +n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn +FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo +5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq +3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 +poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y +eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km +-----END CERTIFICATE----- + +China Internet Network Information Center EV Certificates Root +============================================================== +-----BEGIN CERTIFICATE----- +MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV +BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D +aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg +Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG +A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM +PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl +cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y +jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV +98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H +klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 +KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC +7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD +glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 +0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM +7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws +ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 +5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= +-----END CERTIFICATE----- + +Swisscom Root CA 2 +================== +-----BEGIN CERTIFICATE----- +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 +5OfNeOI5wSsSnqaeG8XmDtkx2Q== +-----END CERTIFICATE----- + +Swisscom Root EV CA 2 +===================== +-----BEGIN CERTIFICATE----- +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 +l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= +-----END CERTIFICATE----- + +CA Disig Root R1 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy +3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 +u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 +m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk +CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa +YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 +vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL +LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX +ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is +XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ +04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR +xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B +LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM +CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb +VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 +YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS +ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix +lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N +UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ +a7+h89n07eLw4+1knj0vllJPgFOL +-----END CERTIFICATE----- + +CA Disig Root R2 +================ +-----BEGIN CERTIFICATE----- +MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw +EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp +ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx +EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp +c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC +w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia +xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 +A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S +GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV +g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa +5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE +koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A +Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i +Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV +HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u +Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM +tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV +sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je +dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 +1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx +mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 +utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 +sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg +UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV +7+ZtsH8tZ/3zbBt1RqPlShfppNcL +-----END CERTIFICATE----- + +ACCVRAIZ1 +========= +-----BEGIN CERTIFICATE----- +MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB +SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 +MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH +UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM +jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 +RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD +aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ +0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG +WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 +8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR +5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J +9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK +Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw +Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu +Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 +VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM +Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA +QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh +AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA +YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj +AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA +IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk +aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 +dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 +MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI +hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E +R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN +YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 +nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ +TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 +sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h +I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg +Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd +3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p +EfbRD0tVNEYqi4Y7 +-----END CERTIFICATE----- + +TWCA Global Root CA +=================== +-----BEGIN CERTIFICATE----- +MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT +CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD +QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK +EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg +Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C +nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV +r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR +Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV +tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W +KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 +sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p +yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn +kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI +zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC +AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g +cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn +LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M +8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg +/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg +lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP +A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m +i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 +EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 +zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= +-----END CERTIFICATE----- diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php new file mode 100644 index 000000000..dbd4c1841 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php @@ -0,0 +1,157 @@ +createRequest($method, $url, null, null, $options); + + if (isset($options['stream'])) { + if ($options['stream'] instanceof StreamRequestFactoryInterface) { + return $options['stream']->fromRequest($request); + } elseif ($options['stream'] == true) { + $streamFactory = new PhpStreamRequestFactory(); + return $streamFactory->fromRequest($request); + } + } + + return $request->send(); + } + + /** + * Send a GET request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function get($url, $options = array()) + { + return self::request('GET', $url, $options); + } + + /** + * Send a HEAD request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function head($url, $options = array()) + { + return self::request('HEAD', $url, $options); + } + + /** + * Send a DELETE request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function delete($url, $options = array()) + { + return self::request('DELETE', $url, $options); + } + + /** + * Send a POST request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function post($url, $options = array()) + { + return self::request('POST', $url, $options); + } + + /** + * Send a PUT request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function put($url, $options = array()) + { + return self::request('PUT', $url, $options); + } + + /** + * Send a PATCH request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function patch($url, $options = array()) + { + return self::request('PATCH', $url, $options); + } + + /** + * Send an OPTIONS request + * + * @param string $url URL of the request + * @param array $options Array of request options + * + * @return \Guzzle\Http\Message\Response + * @see Guzzle::request for a list of available options + */ + public static function options($url, $options = array()) + { + return self::request('OPTIONS', $url, $options); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php new file mode 100644 index 000000000..6a4e77245 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php @@ -0,0 +1,554 @@ + null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + if (false === ($parts = parse_url($url))) { + throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); + } + + $parts += $defaults; + + // Convert the query string into a QueryString object + if ($parts['query'] || 0 !== strlen($parts['query'])) { + $parts['query'] = QueryString::fromString($parts['query']); + } + + return new static($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (isset($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . ':'; + } + + if (isset($parts['host'])) { + $url .= '//'; + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) + && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) + ) { + $url .= ':' . $parts['port']; + } + } + + // Add the path component if present + if (isset($parts['path']) && 0 !== strlen($parts['path'])) { + // Always ensure that the path begins with '/' if set and something is before the path + if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (isset($parts['query'])) { + $url .= '?' . $parts['query']; + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param QueryString|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + if (!$query) { + $this->query = new QueryString(); + } else { + $this->setQuery($query); + } + $this->setPath($path); + } + + /** + * Clone the URL + */ + public function __clone() + { + $this->query = clone $this->query; + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return self::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + $query = (string) $this->query; + + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->getPath(), + 'query' => $query !== '' ? $query : null, + 'fragment' => $this->fragment, + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + + return $this; + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Url + */ + public function setScheme($scheme) + { + if ($this->scheme == 'http' && $this->port == 80) { + $this->port = null; + } elseif ($this->scheme == 'https' && $this->port == 443) { + $this->port = null; + } + + $this->scheme = $scheme; + + return $this; + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + * + * @return Url + */ + public function setPort($port) + { + $this->port = $port; + + return $this; + } + + /** + * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif ($this->scheme == 'http') { + return 80; + } elseif ($this->scheme == 'https') { + return 443; + } + + return null; + } + + /** + * Set the path part of the URL + * + * @param array|string $path Path string or array of path segments + * + * @return Url + */ + public function setPath($path) + { + static $pathReplace = array(' ' => '%20', '?' => '%3F'); + if (is_array($path)) { + $path = '/' . implode('/', $path); + } + + $this->path = strtr($path, $pathReplace); + + return $this; + } + + /** + * Normalize the URL so that double slashes and relative paths are removed + * + * @return Url + */ + public function normalizePath() + { + if (!$this->path || $this->path == '/' || $this->path == '*') { + return $this; + } + + $results = array(); + $segments = $this->getPathSegments(); + foreach ($segments as $segment) { + if ($segment == '..') { + array_pop($results); + } elseif ($segment != '.' && $segment != '') { + $results[] = $segment; + } + } + + // Combine the normalized parts and add the leading slash if needed + $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results); + + // Add the trailing slash if necessary + if ($this->path != '/' && end($segments) == '') { + $this->path .= '/'; + } + + return $this; + } + + /** + * Add a relative path to the currently set path. + * + * @param string $relativePath Relative path to add + * + * @return Url + */ + public function addPath($relativePath) + { + if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) { + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + $this->setPath(str_replace('//', '/', $this->path . $relativePath)); + } + + return $this; + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return array_slice(explode('/', $this->getPath()), 1); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + * + * @return Url + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + * + * @return Url + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a QueryString object + * + * @return QueryString + */ + public function getQuery() + { + return $this->query; + } + + /** + * Set the query part of the URL + * + * @param QueryString|string|array $query Query to set + * + * @return Url + */ + public function setQuery($query) + { + if (is_string($query)) { + $output = null; + parse_str($query, $output); + $this->query = new QueryString($output); + } elseif (is_array($query)) { + $this->query = new QueryString($query); + } elseif ($query instanceof QueryString) { + $this->query = $query; + } + + return $this; + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + * + * @return Url + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. + * + * @param string $url Relative URL to combine with + * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first + * released, Guzzle used an incorrect algorithm for combining relative URL paths. In + * order to not break users, we introduced this flag to allow the merging of URLs based + * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with + * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would + * become "http://a.com/foo/baz/bar". + * @return Url + * @throws InvalidArgumentException + * @link http://tools.ietf.org/html/rfc3986#section-5.4 + */ + public function combine($url, $strictRfc3986 = false) + { + $url = self::factory($url); + + // Use the more absolute URL as the base URL + if (!$this->isAbsolute() && $url->isAbsolute()) { + $url = $url->combine($this); + } + + // Passing a URL with a scheme overrides everything + if ($buffer = $url->getScheme()) { + $this->scheme = $buffer; + $this->host = $url->getHost(); + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + // Setting a host overrides the entire rest of the URL + if ($buffer = $url->getHost()) { + $this->host = $buffer; + $this->port = $url->getPort(); + $this->username = $url->getUsername(); + $this->password = $url->getPassword(); + $this->path = $url->getPath(); + $this->query = $url->getQuery(); + $this->fragment = $url->getFragment(); + return $this; + } + + $path = $url->getPath(); + $query = $url->getQuery(); + + if (!$path) { + if (count($query)) { + $this->addQuery($query, $strictRfc3986); + } + } else { + if ($path[0] == '/') { + $this->path = $path; + } elseif ($strictRfc3986) { + $this->path .= '/../' . $path; + } else { + $this->path .= '/' . $path; + } + $this->normalizePath(); + $this->addQuery($query, $strictRfc3986); + } + + $this->fragment = $url->getFragment(); + + return $this; + } + + private function addQuery(QueryString $new, $strictRfc386) + { + if (!$strictRfc386) { + $new->merge($this->query); + } + + $this->query = $new; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json new file mode 100644 index 000000000..9384a5bf9 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json @@ -0,0 +1,32 @@ +{ + "name": "guzzle/http", + "description": "HTTP libraries used by Guzzle", + "homepage": "http://guzzlephp.org/", + "keywords": ["http client", "http", "client", "Guzzle", "curl"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": "self.version", + "guzzle/parser": "self.version", + "guzzle/stream": "self.version" + }, + "suggest": { + "ext-curl": "*" + }, + "autoload": { + "psr-0": { "Guzzle\\Http": "" } + }, + "target-dir": "Guzzle/Http", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php new file mode 100644 index 000000000..c6997734c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php @@ -0,0 +1,38 @@ + array(), + 'camel' => array() + ); + + /** @var int Max entries per cache */ + protected $maxCacheSize; + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param int $maxCacheSize Maximum number of cached items to hold per cache + */ + public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) + { + $this->decoratedInflector = $inflector; + $this->maxCacheSize = $maxCacheSize; + } + + public function snake($word) + { + if (!isset($this->cache['snake'][$word])) { + $this->pruneCache('snake'); + $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); + } + + return $this->cache['snake'][$word]; + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + if (!isset($this->cache['camel'][$word])) { + $this->pruneCache('camel'); + $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); + } + + return $this->cache['camel'][$word]; + } + + /** + * Prune one of the named caches by removing 20% of the cache if it is full + * + * @param string $cache Type of cache to prune + */ + protected function pruneCache($cache) + { + if (count($this->cache[$cache]) == $this->maxCacheSize) { + $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php new file mode 100644 index 000000000..db37e4fe4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php @@ -0,0 +1,59 @@ + array(), + 'camel' => array() + ); + + /** @var InflectorInterface Decorated inflector */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param array $snake Hash of pre-computed camel to snake + * @param array $camel Hash of pre-computed snake to camel + * @param bool $mirror Mirror snake and camel reflections + */ + public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) + { + if ($mirror) { + $camel = array_merge(array_flip($snake), $camel); + $snake = array_merge(array_flip($camel), $snake); + } + + $this->decoratedInflector = $inflector; + $this->mapping = array( + 'snake' => $snake, + 'camel' => $camel + ); + } + + public function snake($word) + { + return isset($this->mapping['snake'][$word]) + ? $this->mapping['snake'][$word] + : $this->decoratedInflector->snake($word); + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + return isset($this->mapping['camel'][$word]) + ? $this->mapping['camel'][$word] + : $this->decoratedInflector->camel($word); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json new file mode 100644 index 000000000..93f9e7b72 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json @@ -0,0 +1,26 @@ +{ + "name": "guzzle/inflection", + "description": "Guzzle inflection component", + "homepage": "http://guzzlephp.org/", + "keywords": ["inflection", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Inflection": "" } + }, + "target-dir": "Guzzle/Inflection", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php new file mode 100644 index 000000000..1b6bd7e53 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php @@ -0,0 +1,19 @@ +getArrayIterator()->append($iterator); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php new file mode 100644 index 000000000..d76cdd439 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php @@ -0,0 +1,56 @@ +chunkSize = $chunkSize; + } + + public function rewind() + { + parent::rewind(); + $this->next(); + } + + public function next() + { + $this->chunk = array(); + for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) { + $this->chunk[] = parent::current(); + parent::next(); + } + } + + public function current() + { + return $this->chunk; + } + + public function valid() + { + return (bool) $this->chunk; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php new file mode 100644 index 000000000..b103367b6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php @@ -0,0 +1,36 @@ +callback = $callback; + } + + public function accept() + { + return call_user_func($this->callback, $this->current()); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php new file mode 100644 index 000000000..7e586bda6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php @@ -0,0 +1,34 @@ +callback = $callback; + } + + public function current() + { + return call_user_func($this->callback, parent::current()); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php new file mode 100644 index 000000000..de4ab0360 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php @@ -0,0 +1,27 @@ +getInnerIterator(); + while ($i instanceof \OuterIterator) { + $i = $i->getInnerIterator(); + } + + return call_user_func_array(array($i, $name), $args); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md new file mode 100644 index 000000000..8bb7e08e2 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md @@ -0,0 +1,25 @@ +Guzzle Iterator +=============== + +Provides useful Iterators and Iterator decorators + +- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays +- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available +- MapIterator: Maps values before yielding +- MethodProxyIterator: Proxies missing method calls to the innermost iterator + +### Installing via Composer + +```bash +# Install Composer +curl -sS https://getcomposer.org/installer | php + +# Add Guzzle as a dependency +php composer.phar require guzzle/iterator:~3.0 +``` + +After installing, you need to require Composer's autoloader: + +```php +require 'vendor/autoload.php'; +``` diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json new file mode 100644 index 000000000..ee1737987 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/iterator", + "description": "Provides helpful iterators and iterator decorators", + "keywords": ["iterator", "guzzle"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/common": ">=2.8.0" + }, + "autoload": { + "psr-0": { "Guzzle\\Iterator": "/" } + }, + "target-dir": "Guzzle/Iterator", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php new file mode 100644 index 000000000..7f6271bcb --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php @@ -0,0 +1,16 @@ +log; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php new file mode 100644 index 000000000..a70fc8d42 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php @@ -0,0 +1,34 @@ +logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); + } + + /** + * Get logged entries + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears logged entries + */ + public function clearLogs() + { + $this->logs = array(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php new file mode 100644 index 000000000..d4bb73f21 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php @@ -0,0 +1,23 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + call_user_func($this->log, $message, $priority, $extras); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php new file mode 100644 index 000000000..d7ac4ea7c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php @@ -0,0 +1,18 @@ +>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}"; + const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}'; + + /** + * @var string Template used to format log messages + */ + protected $template; + + /** + * @param string $template Log message template + */ + public function __construct($template = self::DEFAULT_FORMAT) + { + $this->template = $template ?: self::DEFAULT_FORMAT; + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->template = $template; + + return $this; + } + + /** + * Returns a formatted message + * + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received + * @param CurlHandle $handle Curl handle associated with the message + * @param array $customData Associative array of custom template data + * + * @return string + */ + public function format( + RequestInterface $request, + Response $response = null, + CurlHandle $handle = null, + array $customData = array() + ) { + $cache = $customData; + + return preg_replace_callback( + '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', + function (array $matches) use ($request, $response, $handle, &$cache) { + + if (array_key_exists($matches[1], $cache)) { + return $cache[$matches[1]]; + } + + $result = ''; + switch ($matches[1]) { + case 'request': + $result = (string) $request; + break; + case 'response': + $result = (string) $response; + break; + case 'req_body': + $result = $request instanceof EntityEnclosingRequestInterface + ? (string) $request->getBody() : ''; + break; + case 'res_body': + $result = $response ? $response->getBody(true) : ''; + break; + case 'ts': + $result = gmdate('c'); + break; + case 'method': + $result = $request->getMethod(); + break; + case 'url': + $result = (string) $request->getUrl(); + break; + case 'resource': + $result = $request->getResource(); + break; + case 'protocol': + $result = 'HTTP'; + break; + case 'version': + $result = $request->getProtocolVersion(); + break; + case 'host': + $result = $request->getHost(); + break; + case 'hostname': + $result = gethostname(); + break; + case 'port': + $result = $request->getPort(); + break; + case 'code': + $result = $response ? $response->getStatusCode() : ''; + break; + case 'phrase': + $result = $response ? $response->getReasonPhrase() : ''; + break; + case 'connect_time': + $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) + ? $handle->getInfo(CURLINFO_CONNECT_TIME) + : ($response ? $response->getInfo('connect_time') : ''); + break; + case 'total_time': + $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) + ? $handle->getInfo(CURLINFO_TOTAL_TIME) + : ($response ? $response->getInfo('total_time') : ''); + break; + case 'curl_error': + $result = $handle ? $handle->getError() : ''; + break; + case 'curl_code': + $result = $handle ? $handle->getErrorNo() : ''; + break; + case 'curl_stderr': + $result = $handle ? $handle->getStderr() : ''; + break; + default: + if (strpos($matches[1], 'req_header_') === 0) { + $result = $request->getHeader(substr($matches[1], 11)); + } elseif ($response && strpos($matches[1], 'res_header_') === 0) { + $result = $response->getHeader(substr($matches[1], 11)); + } + } + + $cache[$matches[1]] = $result; + return $result; + }, + $this->template + ); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php new file mode 100644 index 000000000..6afe7b62a --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php @@ -0,0 +1,34 @@ + Logger::DEBUG, + LOG_INFO => Logger::INFO, + LOG_WARNING => Logger::WARNING, + LOG_ERR => Logger::ERROR, + LOG_CRIT => Logger::CRITICAL, + LOG_ALERT => Logger::ALERT + ); + + public function __construct(Logger $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->addRecord(self::$mapping[$priority], $message, $extras); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php new file mode 100644 index 000000000..38a2b600d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php @@ -0,0 +1,36 @@ + LogLevel::DEBUG, + LOG_INFO => LogLevel::INFO, + LOG_WARNING => LogLevel::WARNING, + LOG_ERR => LogLevel::ERROR, + LOG_CRIT => LogLevel::CRITICAL, + LOG_ALERT => LogLevel::ALERT + ); + + public function __construct(LoggerInterface $logObject) + { + $this->log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log(self::$mapping[$priority], $message, $extras); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php new file mode 100644 index 000000000..0ea8e3b1d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php @@ -0,0 +1,24 @@ +log = $logObject; + Version::warn(__CLASS__ . ' is deprecated'); + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($message, $priority, $extras); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php new file mode 100644 index 000000000..863f6a1c4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php @@ -0,0 +1,21 @@ +log = $logObject; + } + + public function log($message, $priority = LOG_INFO, $extras = array()) + { + $this->log->log($priority, $message, $extras); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json new file mode 100644 index 000000000..a8213e8b4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json @@ -0,0 +1,29 @@ +{ + "name": "guzzle/log", + "description": "Guzzle log adapter component", + "homepage": "http://guzzlephp.org/", + "keywords": ["log", "adapter", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Log": "" } + }, + "suggest": { + "guzzle/http": "self.version" + }, + "target-dir": "Guzzle/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php new file mode 100644 index 000000000..4349eeb38 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php @@ -0,0 +1,131 @@ + 'Domain', + 'path' => 'Path', + 'max_age' => 'Max-Age', + 'expires' => 'Expires', + 'version' => 'Version', + 'secure' => 'Secure', + 'port' => 'Port', + 'discard' => 'Discard', + 'comment' => 'Comment', + 'comment_url' => 'Comment-Url', + 'http_only' => 'HttpOnly' + ); + + public function parseCookie($cookie, $host = null, $path = null, $decode = false) + { + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return false; + } + + // Create the default return array + $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( + 'cookies' => array(), + 'data' => array(), + 'path' => null, + 'http_only' => false, + 'discard' => false, + 'domain' => $host + )); + $foundNonCookies = 0; + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + + if (count($cookieParts) == 1) { + // Can be a single value (e.g. secure, httpOnly) + $value = true; + } else { + // Be sure to strip wrapping quotes + $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); + if ($decode) { + $value = urldecode($value); + } + } + + // Only check for non-cookies when cookies have been found + if (!empty($data['cookies'])) { + foreach (self::$cookieParts as $mapValue => $search) { + if (!strcasecmp($search, $key)) { + $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; + $foundNonCookies++; + continue 2; + } + } + } + + // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a + // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. + $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; + } + + // Calculate the expires date + if (!$data['expires'] && $data['max_age']) { + $data['expires'] = time() + (int) $data['max_age']; + } + + // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4 + // "If the attribute-value is empty or if the first character of the + // attribute-value is not %x2F ("/"): + // Let cookie-path be the default-path. + // Otherwise: + // Let cookie-path be the attribute-value." + if (!$data['path'] || substr($data['path'], 0, 1) !== '/') { + $data['path'] = $this->getDefaultPath($path); + } + + return $data; + } + + /** + * Get default cookie path according to RFC 6265 + * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match + * + * @param string $path Request uri-path + * + * @return string + */ + protected function getDefaultPath($path) { + // "The user agent MUST use an algorithm equivalent to the following algorithm + // to compute the default-path of a cookie:" + + // "2. If the uri-path is empty or if the first character of the uri-path is not + // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. + if (empty($path) || substr($path, 0, 1) !== '/') { + return '/'; + } + + // "3. If the uri-path contains no more than one %x2F ("/") character, output + // %x2F ("/") and skip the remaining step." + if ($path === "/") { + return $path; + } + + $rightSlashPos = strrpos($path, '/'); + if ($rightSlashPos === 0) { + return "/"; + } + + // "4. Output the characters of the uri-path from the first character up to, + // but not including, the right-most %x2F ("/")." + return substr($path, 0, $rightSlashPos); + + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php new file mode 100644 index 000000000..d21ffe21c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php @@ -0,0 +1,33 @@ + $requestUrl, + 'scheme' => 'http' + ); + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = null; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php new file mode 100644 index 000000000..104740068 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php @@ -0,0 +1,110 @@ +parseMessage($message); + + // Parse the protocol and protocol version + if (isset($parts['start_line'][2])) { + $startParts = explode('/', $parts['start_line'][2]); + $protocol = strtoupper($startParts[0]); + $version = isset($startParts[1]) ? $startParts[1] : '1.1'; + } else { + $protocol = 'HTTP'; + $version = '1.1'; + } + + $parsed = array( + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => $protocol, + 'version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = $this->parseMessage($message); + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return array( + 'protocol' => $protocol, + 'version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array + */ + protected function parseMessage($message) + { + $startLine = null; + $headers = array(); + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + $startLine = explode(' ', $line, 3); + } elseif (strpos($line, ':')) { + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = array($headers[$key], $value); + } else { + $headers[$key][] = $value; + } + } + } + + return array( + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php new file mode 100644 index 000000000..cc448088d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php @@ -0,0 +1,27 @@ + $parts->requestMethod, + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'headers' => $parts->headers, + 'body' => $parts->body + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); + + return $parsed; + } + + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = http_parse_message($message); + + return array( + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'code' => $parts->responseCode, + 'reason_phrase' => $parts->responseStatus, + 'headers' => $parts->headers, + 'body' => $parts->body + ); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php new file mode 100644 index 000000000..f8386831c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php @@ -0,0 +1,75 @@ + 'Guzzle\\Parser\\Message\\MessageParser', + 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', + 'url' => 'Guzzle\\Parser\\Url\\UrlParser', + 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', + ); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new static; + } + + return self::$instance; + } + + public function __construct() + { + // Use the PECL URI template parser if available + if (extension_loaded('uri_template')) { + $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; + } + } + + /** + * Get a parser by name from an instance + * + * @param string $name Name of the parser to retrieve + * + * @return mixed|null + */ + public function getParser($name) + { + if (!isset($this->instances[$name])) { + if (!isset($this->mapping[$name])) { + return null; + } + $class = $this->mapping[$name]; + $this->instances[$name] = new $class(); + } + + return $this->instances[$name]; + } + + /** + * Register a custom parser by name with the register + * + * @param string $name Name or handle of the parser to register + * @param mixed $parser Instantiated parser to register + */ + public function registerParser($name, $parser) + { + $this->instances[$name] = $parser; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php new file mode 100644 index 000000000..b0764e837 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php @@ -0,0 +1,26 @@ + true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true + ); + + /** @var array Delimiters */ + private static $delims = array( + ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' + ); + + /** @var array Percent encoded delimiters */ + private static $delimsPct = array( + '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', + '%3B', '%3D' + ); + + public function expand($template, array $variables) + { + if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) { + return $template; + } + + $this->template = $template; + $this->variables = $variables; + + return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template); + } + + /** + * Set the regex patten used to expand URI templates + * + * @param string $regexPattern + */ + public function setRegex($regexPattern) + { + $this->regex = $regexPattern; + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + // Check for URI operators + $operator = ''; + + if (isset(self::$operatorHash[$expression[0]])) { + $operator = $expression[0]; + $expression = substr($expression, 1); + } + + $values = explode(',', $expression); + foreach ($values as &$value) { + $value = trim($value); + $varspec = array(); + $substrPos = strpos($value, ':'); + if ($substrPos) { + $varspec['value'] = substr($value, 0, $substrPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $substrPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $value = $varspec; + } + + return array( + 'operator' => $operator, + 'values' => $values + ); + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array( + '+' => '%20', + '%7e' => '~' + ); + + $parsed = self::parseExpression($matches[1]); + $replacements = array(); + + $prefix = $parsed['operator']; + $joiner = $parsed['operator']; + $useQueryString = false; + if ($parsed['operator'] == '?') { + $joiner = '&'; + $useQueryString = true; + } elseif ($parsed['operator'] == '&') { + $useQueryString = true; + } elseif ($parsed['operator'] == '#') { + $joiner = ','; + } elseif ($parsed['operator'] == ';') { + $useQueryString = true; + } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { + $joiner = ','; + $prefix = ''; + } + + foreach ($parsed['values'] as $value) { + + if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQueryString = $useQueryString; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested structures + $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQueryString) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if (empty($variable)) { + $actuallyUseQueryString = false; + } elseif ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode modifier with an associative array + $actuallyUseQueryString = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the explode modifier is not set, then the + // result must be a comma separated list of keys followed by their respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQueryString) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Removes percent encoding on reserved characters (used with + and # modifiers) + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php new file mode 100644 index 000000000..c81d51548 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php @@ -0,0 +1,21 @@ +utf8 = $utf8; + } + + public function parseUrl($url) + { + Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); + + static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, + 'user' => null, 'pass' => null, 'fragment' => null); + + $parts = parse_url($url); + + // Need to handle query parsing specially for UTF-8 requirements + if ($this->utf8 && isset($parts['query'])) { + $queryPos = strpos($url, '?'); + if (isset($parts['fragment'])) { + $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); + } else { + $parts['query'] = substr($url, $queryPos + 1); + } + } + + return $parts + $defaults; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php new file mode 100644 index 000000000..89ac4b307 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php @@ -0,0 +1,19 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Parser": "" } + }, + "target-dir": "Guzzle/Parser", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php new file mode 100644 index 000000000..ae5941873 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php @@ -0,0 +1,84 @@ + 'onBeforeSend', + 'request.exception' => 'onRequestTimeout', + 'request.sent' => 'onRequestSent', + 'curl.callback.progress' => 'onCurlProgress' + ); + } + + /** + * Event used to ensure that progress callback are emitted from the curl handle's request mediator. + * + * @param Event $event + */ + public function onBeforeSend(Event $event) + { + // Ensure that progress callbacks are dispatched + $event['request']->getCurlOptions()->set('progress', true); + } + + /** + * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to + * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with + * transmitting the request, and tell curl not download a body. + * + * @param Event $event + */ + public function onCurlProgress(Event $event) + { + if ($event['handle'] && + ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded'])) + ) { + // Timeout after 1ms + curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1); + // Even if the response is quick, tell curl not to download the body. + // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the + // request method is not converted to a HEAD request before the request was sent via curl. + if ($event['uploaded']) { + curl_setopt($event['handle'], CURLOPT_NOBODY, true); + } + } + } + + /** + * Event emitted when a curl exception occurs. Ignore the exception and set a mock response. + * + * @param Event $event + */ + public function onRequestTimeout(Event $event) + { + if ($event['exception'] instanceof CurlException) { + $event['request']->setResponse(new Response(200, array( + 'X-Guzzle-Async' => 'Did not wait for the response' + ))); + } + } + + /** + * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the + * caller that there is no body in the message. + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + // Let the caller know this was meant to be async + $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json new file mode 100644 index 000000000..dc3fc5bf8 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-async", + "description": "Guzzle async request plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Async": "" } + }, + "target-dir": "Guzzle/Plugin/Async", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php new file mode 100644 index 000000000..0a8598345 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php @@ -0,0 +1,91 @@ +next = $next; + } + + /** + * Get the next backoff strategy in the chain + * + * @return AbstractBackoffStrategy|null + */ + public function getNext() + { + return $this->next; + } + + public function getBackoffPeriod( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ) { + $delay = $this->getDelay($retries, $request, $response, $e); + if ($delay === false) { + // The strategy knows that this must not be retried + return false; + } elseif ($delay === null) { + // If the strategy is deferring a decision and the next strategy will not make a decision then return false + return !$this->next || !$this->next->makesDecision() + ? false + : $this->next->getBackoffPeriod($retries, $request, $response, $e); + } elseif ($delay === true) { + // if the strategy knows that it must retry but is deferring to the next to determine the delay + if (!$this->next) { + return 0; + } else { + $next = $this->next; + while ($next->makesDecision() && $next->getNext()) { + $next = $next->getNext(); + } + return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; + } + } else { + return $delay; + } + } + + /** + * Check if the strategy does filtering and makes decisions on whether or not to retry. + * + * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff + * decision. + * + * @return bool + */ + abstract public function makesDecision(); + + /** + * Implement the concrete strategy + * + * @param int $retries Number of retries of the request + * @param RequestInterface $request Request that was sent + * @param Response $response Response that was received. Note that there may not be a response + * @param HttpException $e Exception that was encountered if any + * + * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true + * or null to defer to the next strategy if available, and if not, return 0. + */ + abstract protected function getDelay( + $retries, + RequestInterface $request, + Response $response = null, + HttpException $e = null + ); +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php new file mode 100644 index 000000000..6ebee6c1a --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php @@ -0,0 +1,40 @@ +errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); + $this->next = $next; + } + + /** + * Get the default failure codes to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return static::$defaultErrorCodes; + } + + public function makesDecision() + { + return true; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php new file mode 100644 index 000000000..ec54c289e --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php @@ -0,0 +1,76 @@ +logger = $logger; + $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); + } + + public static function getSubscribedEvents() + { + return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); + } + + /** + * Set the template to use for logging + * + * @param string $template Log message template + * + * @return self + */ + public function setTemplate($template) + { + $this->formatter->setTemplate($template); + + return $this; + } + + /** + * Called when a request is being retried + * + * @param Event $event Event emitted + */ + public function onRequestRetry(Event $event) + { + $this->logger->log($this->formatter->format( + $event['request'], + $event['response'], + $event['handle'], + array( + 'retries' => $event['retries'], + 'delay' => $event['delay'] + ) + )); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php new file mode 100644 index 000000000..99ace0538 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php @@ -0,0 +1,126 @@ +strategy = $strategy; + } + + /** + * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors + * + * @param int $maxRetries Maximum number of retries + * @param array $httpCodes HTTP response codes to retry + * @param array $curlCodes cURL error codes to retry + * + * @return self + */ + public static function getExponentialBackoff( + $maxRetries = 3, + array $httpCodes = null, + array $curlCodes = null + ) { + return new self(new TruncatedBackoffStrategy($maxRetries, + new HttpBackoffStrategy($httpCodes, + new CurlBackoffStrategy($curlCodes, + new ExponentialBackoffStrategy() + ) + ) + )); + } + + public static function getAllEvents() + { + return array(self::RETRY_EVENT); + } + + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + + $params = $request->getParams(); + $retries = (int) $params->get(self::RETRY_PARAM); + $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); + + if ($delay !== false) { + // Calculate how long to wait until the request should be retried + $params->set(self::RETRY_PARAM, ++$retries) + ->set(self::DELAY_PARAM, microtime(true) + $delay); + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $this->dispatch(self::RETRY_EVENT, array( + 'request' => $request, + 'response' => $response, + 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, + 'retries' => $retries, + 'delay' => $delay + )); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. This is required for cURL to know that we + // want to retry sending the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php new file mode 100644 index 000000000..4e590dbe0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php @@ -0,0 +1,30 @@ +callback = $callback; + $this->decision = (bool) $decision; + $this->next = $next; + } + + public function makesDecision() + { + return $this->decision; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return call_user_func($this->callback, $retries, $request, $response, $e); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php new file mode 100644 index 000000000..061d2a407 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php @@ -0,0 +1,34 @@ +delay = $delay; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $this->delay; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php new file mode 100644 index 000000000..a584ed4a2 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php @@ -0,0 +1,28 @@ +errorCodes[$e->getErrorNo()]) ? true : null; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php new file mode 100644 index 000000000..fb2912d50 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php @@ -0,0 +1,25 @@ +isSuccessful()) { + return false; + } else { + return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php new file mode 100644 index 000000000..b35e8a490 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php @@ -0,0 +1,36 @@ +step = $step; + } + + public function makesDecision() + { + return false; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries * $this->step; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php new file mode 100644 index 000000000..4fd73fedf --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php @@ -0,0 +1,25 @@ +errorCodes[$response->getReasonPhrase()]) ? true : null; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php new file mode 100644 index 000000000..3608f3584 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php @@ -0,0 +1,36 @@ +max = $maxRetries; + $this->next = $next; + } + + public function makesDecision() + { + return true; + } + + protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) + { + return $retries < $this->max ? null : false; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json new file mode 100644 index 000000000..91c122cb4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-backoff", + "description": "Guzzle backoff retry plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Backoff": "" } + }, + "target-dir": "Guzzle/Plugin/Backoff", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php new file mode 100644 index 000000000..7790f8844 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php @@ -0,0 +1,11 @@ + new DefaultCacheStorage($options)); + } elseif ($options instanceof CacheStorageInterface) { + $options = array('storage' => $options); + } elseif ($options) { + $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options))); + } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) { + // @codeCoverageIgnoreStart + throw new InvalidArgumentException('No cache was provided and Doctrine is not installed'); + // @codeCoverageIgnoreEnd + } + } + + $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false; + + // Add a cache storage if a cache adapter was provided + $this->storage = isset($options['storage']) + ? $options['storage'] + : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); + + if (!isset($options['can_cache'])) { + $this->canCache = new DefaultCanCacheStrategy(); + } else { + $this->canCache = is_callable($options['can_cache']) + ? new CallbackCanCacheStrategy($options['can_cache']) + : $options['can_cache']; + } + + // Use the provided revalidation strategy or the default + $this->revalidation = isset($options['revalidation']) + ? $options['revalidation'] + : new DefaultRevalidation($this->storage, $this->canCache); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -255), + 'request.sent' => array('onRequestSent', 255), + 'request.error' => array('onRequestError', 0), + 'request.exception' => array('onRequestException', 0), + ); + } + + /** + * Check if a response in cache will satisfy the request before sending + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + if (!$this->canCache->canCacheRequest($request)) { + switch ($request->getMethod()) { + case 'PURGE': + $this->purge($request); + $request->setResponse(new Response(200, array(), 'purged')); + break; + case 'PUT': + case 'POST': + case 'DELETE': + case 'PATCH': + if ($this->autoPurge) { + $this->purge($request); + } + } + return; + } + + if ($response = $this->storage->fetch($request)) { + $params = $request->getParams(); + $params['cache.lookup'] = true; + $response->setHeader( + 'Age', + time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now') + ); + // Validate that the response satisfies the request + if ($this->canResponseSatisfyRequest($request, $response)) { + if (!isset($params['cache.hit'])) { + $params['cache.hit'] = true; + } + $request->setResponse($response); + } + } + } + + /** + * If possible, store a response in cache after sending + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + + if ($request->getParams()->get('cache.hit') === null && + $this->canCache->canCacheRequest($request) && + $this->canCache->canCacheResponse($response) + ) { + $this->storage->cache($request, $response); + } + + $this->addResponseHeaders($request, $response); + } + + /** + * If possible, return a cache response on an error + * + * @param Event $event + */ + public function onRequestError(Event $event) + { + $request = $event['request']; + + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader( + 'Age', + time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now') + ); + + if ($this->canResponseSatisfyFailedRequest($request, $response)) { + $request->getParams()->set('cache.hit', 'error'); + $this->addResponseHeaders($request, $response); + $event['response'] = $response; + $event->stopPropagation(); + } + } + } + + /** + * If possible, set a cache response on a cURL exception + * + * @param Event $event + * + * @return null + */ + public function onRequestException(Event $event) + { + if (!$event['exception'] instanceof CurlException) { + return; + } + + $request = $event['request']; + if (!$this->canCache->canCacheRequest($request)) { + return; + } + + if ($response = $this->storage->fetch($request)) { + $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now')); + if (!$this->canResponseSatisfyFailedRequest($request, $response)) { + return; + } + $request->getParams()->set('cache.hit', 'error'); + $request->setResponse($response); + $this->addResponseHeaders($request, $response); + $event->stopPropagation(); + } + } + + /** + * Check if a cache response satisfies a request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyRequest(RequestInterface $request, Response $response) + { + $responseAge = $response->calculateAge(); + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + + // Check the request's max-age header against the age of the response + if ($reqc && $reqc->hasDirective('max-age') && + $responseAge > $reqc->getDirective('max-age')) { + return false; + } + + // Check the response's max-age header + if ($response->isFresh() === false) { + $maxStale = $reqc ? $reqc->getDirective('max-stale') : null; + if (null !== $maxStale) { + if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { + return false; + } + } elseif ($resc && $resc->hasDirective('max-age') + && $responseAge > $resc->getDirective('max-age') + ) { + return false; + } + } + + if ($this->revalidation->shouldRevalidate($request, $response)) { + try { + return $this->revalidation->revalidate($request, $response); + } catch (CurlException $e) { + $request->getParams()->set('cache.hit', 'error'); + return $this->canResponseSatisfyFailedRequest($request, $response); + } + } + + return true; + } + + /** + * Check if a cache response satisfies a failed request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response) + { + $reqc = $request->getHeader('Cache-Control'); + $resc = $response->getHeader('Cache-Control'); + $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null; + $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null; + + if (!$requestStaleIfError && !$responseStaleIfError) { + return false; + } + + if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) { + return false; + } + + if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) { + return false; + } + + return true; + } + + /** + * Purge all cache entries for a given URL + * + * @param string $url URL to purge + */ + public function purge($url) + { + // BC compatibility with previous version that accepted a Request object + $url = $url instanceof RequestInterface ? $url->getUrl() : $url; + $this->storage->purge($url); + } + + /** + * Add the plugin's headers to a response + * + * @param RequestInterface $request Request + * @param Response $response Response to add headers to + */ + protected function addResponseHeaders(RequestInterface $request, Response $response) + { + $params = $request->getParams(); + $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); + + $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; + if ($header = $response->getHeader('X-Cache-Lookup')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $lookup; + $response->setHeader('X-Cache-Lookup', array_unique($values)); + } else { + $response->setHeader('X-Cache-Lookup', $lookup); + } + + if ($params['cache.hit'] === true) { + $xcache = 'HIT from GuzzleCache'; + } elseif ($params['cache.hit'] == 'error') { + $xcache = 'HIT_ERROR from GuzzleCache'; + } else { + $xcache = 'MISS from GuzzleCache'; + } + + if ($header = $response->getHeader('X-Cache')) { + // Don't add duplicates + $values = $header->toArray(); + $values[] = $xcache; + $response->setHeader('X-Cache', array_unique($values)); + } else { + $response->setHeader('X-Cache', $xcache); + } + + if ($response->isFresh() === false) { + $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); + if ($params['cache.hit'] === 'error') { + $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php new file mode 100644 index 000000000..f3d915458 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php @@ -0,0 +1,43 @@ +requestCallback = $requestCallback; + $this->responseCallback = $responseCallback; + } + + public function canCacheRequest(RequestInterface $request) + { + return $this->requestCallback + ? call_user_func($this->requestCallback, $request) + : parent::canCacheRequest($request); + } + + public function canCacheResponse(Response $response) + { + return $this->responseCallback + ? call_user_func($this->responseCallback, $response) + : parent::canCacheResponse($response); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php new file mode 100644 index 000000000..6e01a8e74 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php @@ -0,0 +1,30 @@ +getParams()->get(self::CACHE_KEY); + + if (!$key) { + + $cloned = clone $request; + $cloned->removeHeader('Cache-Control'); + + // Check to see how and if the key should be filtered + foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { + $pieces = array_map('trim', explode('=', $part)); + if (isset($pieces[1])) { + foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { + if ($pieces[0] == 'header') { + $cloned->removeHeader($remove); + } elseif ($pieces[0] == 'query') { + $cloned->getQuery()->remove($remove); + } + } + } + } + + $raw = (string) $cloned; + $key = 'GZ' . md5($raw); + $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); + } + + return $key; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php new file mode 100644 index 000000000..555c9b79c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php @@ -0,0 +1,251 @@ +cache = CacheAdapterFactory::fromCache($cache); + $this->defaultTtl = $defaultTtl; + $this->keyPrefix = $keyPrefix; + } + + public function cache(RequestInterface $request, Response $response) + { + $currentTime = time(); + $ttl = $request->getParams()->get('cache.override_ttl') ?: $response->getMaxAge() ?: $this->defaultTtl; + + if ($cacheControl = $response->getHeader('Cache-Control')) { + $stale = $cacheControl->getDirective('stale-if-error'); + $ttl += $stale == true ? $ttl : $stale; + } + + // Determine which manifest key should be used + $key = $this->getCacheKey($request); + $persistedRequest = $this->persistHeaders($request); + $entries = array(); + + if ($manifest = $this->cache->fetch($key)) { + // Determine which cache entries should still be in the cache + $vary = $response->getVary(); + foreach (unserialize($manifest) as $entry) { + // Check if the entry is expired + if ($entry[4] < $currentTime) { + continue; + } + $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : ''; + if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) { + $entries[] = $entry; + } + } + } + + // Persist the response body if needed + $bodyDigest = null; + if ($response->getBody() && $response->getBody()->getContentLength() > 0) { + $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody()); + $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl); + } + + array_unshift($entries, array( + $persistedRequest, + $this->persistHeaders($response), + $response->getStatusCode(), + $bodyDigest, + $currentTime + $ttl + )); + + $this->cache->save($key, serialize($entries)); + } + + public function delete(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if ($entries = $this->cache->fetch($key)) { + // Delete each cached body + foreach (unserialize($entries) as $entry) { + if ($entry[3]) { + $this->cache->delete($entry[3]); + } + } + $this->cache->delete($key); + } + } + + public function purge($url) + { + foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) { + $this->delete(new Request($method, $url)); + } + } + + public function fetch(RequestInterface $request) + { + $key = $this->getCacheKey($request); + if (!($entries = $this->cache->fetch($key))) { + return null; + } + + $match = null; + $headers = $this->persistHeaders($request); + $entries = unserialize($entries); + foreach ($entries as $index => $entry) { + if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) { + $match = $entry; + break; + } + } + + if (!$match) { + return null; + } + + // Ensure that the response is not expired + $response = null; + if ($match[4] < time()) { + $response = -1; + } else { + $response = new Response($match[2], $match[1]); + if ($match[3]) { + if ($body = $this->cache->fetch($match[3])) { + $response->setBody($body); + } else { + // The response is not valid because the body was somehow deleted + $response = -1; + } + } + } + + if ($response === -1) { + // Remove the entry from the metadata and update the cache + unset($entries[$index]); + if ($entries) { + $this->cache->save($key, serialize($entries)); + } else { + $this->cache->delete($key); + } + return null; + } + + return $response; + } + + /** + * Hash a request URL into a string that returns cache metadata + * + * @param RequestInterface $request + * + * @return string + */ + protected function getCacheKey(RequestInterface $request) + { + // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) + if ($filter = $request->getParams()->get('cache.key_filter')) { + $url = $request->getUrl(true); + foreach (explode(',', $filter) as $remove) { + $url->getQuery()->remove(trim($remove)); + } + } else { + $url = $request->getUrl(); + } + + return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); + } + + /** + * Create a cache key for a response's body + * + * @param string $url URL of the entry + * @param EntityBodyInterface $body Response body + * + * @return string + */ + protected function getBodyKey($url, EntityBodyInterface $body) + { + return $this->keyPrefix . md5($url) . $body->getContentMd5(); + } + + /** + * Determines whether two Request HTTP header sets are non-varying + * + * @param string $vary Response vary header + * @param array $r1 HTTP header array + * @param array $r2 HTTP header array + * + * @return bool + */ + private function requestsMatch($vary, $r1, $r2) + { + if ($vary) { + foreach (explode(',', $vary) as $header) { + $key = trim(strtolower($header)); + $v1 = isset($r1[$key]) ? $r1[$key] : null; + $v2 = isset($r2[$key]) ? $r2[$key] : null; + if ($v1 !== $v2) { + return false; + } + } + } + + return true; + } + + /** + * Creates an array of cacheable and normalized message headers + * + * @param MessageInterface $message + * + * @return array + */ + private function persistHeaders(MessageInterface $message) + { + // Headers are excluded from the caching (see RFC 2616:13.5.1) + static $noCache = array( + 'age' => true, + 'connection' => true, + 'keep-alive' => true, + 'proxy-authenticate' => true, + 'proxy-authorization' => true, + 'te' => true, + 'trailers' => true, + 'transfer-encoding' => true, + 'upgrade' => true, + 'set-cookie' => true, + 'set-cookie2' => true + ); + + // Clone the response to not destroy any necessary headers when caching + $headers = $message->getHeaders()->getAll(); + $headers = array_diff_key($headers, $noCache); + // Cast the headers to a string + $headers = array_map(function ($h) { return (string) $h; }, $headers); + + return $headers; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php new file mode 100644 index 000000000..3ca1fbf19 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php @@ -0,0 +1,32 @@ +getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) { + return false; + } + + // Never cache requests when using no-store + if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) { + return false; + } + + return true; + } + + public function canCacheResponse(Response $response) + { + return $response->isSuccessful() && $response->canCache(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php new file mode 100644 index 000000000..af33234ee --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php @@ -0,0 +1,174 @@ +storage = $cache; + $this->canCache = $canCache ?: new DefaultCanCacheStrategy(); + } + + public function revalidate(RequestInterface $request, Response $response) + { + try { + $revalidate = $this->createRevalidationRequest($request, $response); + $validateResponse = $revalidate->send(); + if ($validateResponse->getStatusCode() == 200) { + return $this->handle200Response($request, $validateResponse); + } elseif ($validateResponse->getStatusCode() == 304) { + return $this->handle304Response($request, $validateResponse, $response); + } + } catch (BadResponseException $e) { + $this->handleBadResponse($e); + } + + // Other exceptions encountered in the revalidation request are ignored + // in hopes that sending a request to the origin server will fix it + return false; + } + + public function shouldRevalidate(RequestInterface $request, Response $response) + { + if ($request->getMethod() != RequestInterface::GET) { + return false; + } + + $reqCache = $request->getHeader('Cache-Control'); + $resCache = $response->getHeader('Cache-Control'); + + $revalidate = $request->getHeader('Pragma') == 'no-cache' || + ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) || + ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'))); + + // Use the strong ETag validator if available and the response contains no Cache-Control directive + if (!$revalidate && !$resCache && $response->hasHeader('ETag')) { + $revalidate = true; + } + + return $revalidate; + } + + /** + * Handles a bad response when attempting to revalidate + * + * @param BadResponseException $e Exception encountered + * + * @throws BadResponseException + */ + protected function handleBadResponse(BadResponseException $e) + { + // 404 errors mean the resource no longer exists, so remove from + // cache, and prevent an additional request by throwing the exception + if ($e->getResponse()->getStatusCode() == 404) { + $this->storage->delete($e->getRequest()); + throw $e; + } + } + + /** + * Creates a request to use for revalidation + * + * @param RequestInterface $request Request + * @param Response $response Response to revalidate + * + * @return RequestInterface returns a revalidation request + */ + protected function createRevalidationRequest(RequestInterface $request, Response $response) + { + $revalidate = clone $request; + $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control'); + + if ($response->getLastModified()) { + $revalidate->setHeader('If-Modified-Since', $response->getLastModified()); + } + + if ($response->getEtag()) { + $revalidate->setHeader('If-None-Match', $response->getEtag()); + } + + // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations + $dispatcher = $revalidate->getEventDispatcher(); + foreach ($dispatcher->getListeners() as $eventName => $listeners) { + foreach ($listeners as $listener) { + if (is_array($listener) && $listener[0] instanceof CachePlugin) { + $dispatcher->removeListener($eventName, $listener); + } + } + } + + return $revalidate; + } + + /** + * Handles a 200 response response from revalidating. The server does not support validation, so use this response. + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle200Response(RequestInterface $request, Response $validateResponse) + { + $request->setResponse($validateResponse); + if ($this->canCache->canCacheResponse($validateResponse)) { + $this->storage->cache($request, $validateResponse); + } + + return false; + } + + /** + * Handle a 304 response and ensure that it is still valid + * + * @param RequestInterface $request Request that was sent + * @param Response $validateResponse Response received + * @param Response $response Original cached response + * + * @return bool Returns true if valid, false if invalid + */ + protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response) + { + static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); + + // Make sure that this response has the same ETag + if ($validateResponse->getEtag() != $response->getEtag()) { + return false; + } + + // Replace cached headers with any of these headers from the + // origin server that might be more up to date + $modified = false; + foreach ($replaceHeaders as $name) { + if ($validateResponse->hasHeader($name)) { + $modified = true; + $response->setHeader($name, $validateResponse->getHeader($name)); + } + } + + // Store the updated response in cache + if ($modified && $this->canCache->canCacheResponse($response)) { + $this->storage->cache($request, $response); + } + + return true; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php new file mode 100644 index 000000000..88b86f3ca --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php @@ -0,0 +1,19 @@ +=5.3.2", + "guzzle/http": "self.version", + "guzzle/cache": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cache": "" } + }, + "target-dir": "Guzzle/Plugin/Cache", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php new file mode 100644 index 000000000..5218e5f0e --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php @@ -0,0 +1,538 @@ + '', + 'value' => '', + 'domain' => '', + 'path' => '/', + 'expires' => null, + 'max_age' => 0, + 'comment' => null, + 'comment_url' => null, + 'port' => array(), + 'version' => null, + 'secure' => false, + 'discard' => false, + 'http_only' => false + ); + + $this->data = array_merge($defaults, $data); + // Extract the expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the expires date + $this->setExpires(time() + (int) $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires(strtotime($this->getExpires())); + } + } + + /** + * Get the cookie as an array + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return Cookie + */ + public function setName($name) + { + return $this->setData('name', $name); + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return Cookie + */ + public function setValue($value) + { + return $this->setData('value', $value); + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return Cookie + */ + public function setDomain($domain) + { + return $this->setData('domain', $domain); + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return Cookie + */ + public function setPath($path) + { + return $this->setData('path', $path); + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['max_age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return Cookie + */ + public function setMaxAge($maxAge) + { + return $this->setData('max_age', $maxAge); + } + + /** + * The UNIX timestamp when the cookie expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return Cookie + */ + public function setExpires($timestamp) + { + return $this->setData('expires', $timestamp); + } + + /** + * Version of the cookie specification. RFC 2965 is 1 + * + * @return mixed + */ + public function getVersion() + { + return $this->data['version']; + } + + /** + * Set the cookie version + * + * @param string|int $version Version to set + * + * @return Cookie + */ + public function setVersion($version) + { + return $this->setData('version', $version); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return Cookie + */ + public function setSecure($secure) + { + return $this->setData('secure', (bool) $secure); + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return Cookie + */ + public function setDiscard($discard) + { + return $this->setData('discard', $discard); + } + + /** + * Get the comment + * + * @return string|null + */ + public function getComment() + { + return $this->data['comment']; + } + + /** + * Set the comment of the cookie + * + * @param string $comment Cookie comment + * + * @return Cookie + */ + public function setComment($comment) + { + return $this->setData('comment', $comment); + } + + /** + * Get the comment URL of the cookie + * + * @return string|null + */ + public function getCommentUrl() + { + return $this->data['comment_url']; + } + + /** + * Set the comment URL of the cookie + * + * @param string $commentUrl Cookie comment URL for more information + * + * @return Cookie + */ + public function setCommentUrl($commentUrl) + { + return $this->setData('comment_url', $commentUrl); + } + + /** + * Get an array of acceptable ports this cookie can be used with + * + * @return array + */ + public function getPorts() + { + return $this->data['port']; + } + + /** + * Set a list of acceptable ports this cookie can be used with + * + * @param array $ports Array of acceptable ports + * + * @return Cookie + */ + public function setPorts(array $ports) + { + return $this->setData('port', $ports); + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['http_only']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return Cookie + */ + public function setHttpOnly($httpOnly) + { + return $this->setData('http_only', $httpOnly); + } + + /** + * Get an array of extra cookie data + * + * @return array + */ + public function getAttributes() + { + return $this->data['data']; + } + + /** + * Get a specific data point from the extra cookie data + * + * @param string $name Name of the data point to retrieve + * + * @return null|string + */ + public function getAttribute($name) + { + return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; + } + + /** + * Set a cookie data attribute + * + * @param string $name Name of the attribute to set + * @param string $value Value to set + * + * @return Cookie + */ + public function setAttribute($name, $value) + { + $this->data['data'][$name] = $value; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4 + // A request-path path-matches a given cookie-path if at least one of + // the following conditions holds: + + // o The cookie-path and the request-path are identical. + if ($path == $this->getPath()) { + return true; + } + + $pos = stripos($path, $this->getPath()); + if ($pos === 0) { + // o The cookie-path is a prefix of the request-path, and the last + // character of the cookie-path is %x2F ("/"). + if (substr($this->getPath(), -1, 1) === "/") { + return true; + } + + // o The cookie-path is a prefix of the request-path, and the first + // character of the request-path that is not included in the cookie- + // path is a %x2F ("/") character. + if (substr($path, strlen($this->getPath()), 1) === "/") { + return true; + } + } + + return false; + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3 + $cookieDomain = ltrim($this->getDomain(), '.'); + + // Domain not set or exact match. + if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { + return true; + } + + // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3 + if (filter_var($domain, FILTER_VALIDATE_IP)) { + return false; + } + + return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain); + } + + /** + * Check if the cookie is compatible with a specific port + * + * @param int $port Port to check + * + * @return bool + */ + public function matchesPort($port) + { + return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Check if the cookie is valid according to RFC 6265 + * + * @return bool|string Returns true if valid or an error message if invalid + */ + public function validate() + { + // Names must not be empty, but can be 0 + $name = $this->getName(); + if (empty($name) && !is_numeric($name)) { + return 'The cookie name must not be empty'; + } + + // Check if any of the invalid characters are present in the cookie name + if (strpbrk($name, self::getInvalidCharacters()) !== false) { + return 'The cookie name must not contain invalid characters: ' . $name; + } + + // Value must not be empty, but can be 0 + $value = $this->getValue(); + if (empty($value) && !is_numeric($value)) { + return 'The cookie value must not be empty'; + } + + // Domains must not be empty, but can be 0 + // A "0" is not a valid internet domain, but may be used as server name in a private network + $domain = $this->getDomain(); + if (empty($domain) && !is_numeric($domain)) { + return 'The cookie domain must not be empty'; + } + + return true; + } + + /** + * Set a value and return the cookie object + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return Cookie + */ + private function setData($key, $value) + { + $this->data[$key] = $value; + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php new file mode 100644 index 000000000..6b675039f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php @@ -0,0 +1,237 @@ +strictMode = $strictMode; + } + + /** + * Enable or disable strict mode on the cookie jar + * + * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them. + * + * @return self + */ + public function setStrictMode($strictMode) + { + $this->strictMode = $strictMode; + } + + public function remove($domain = null, $path = null, $name = null) + { + $cookies = $this->all($domain, $path, $name, false, false); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { + return !in_array($cookie, $cookies, true); + }); + + return $this; + } + + public function removeTemporary() + { + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + }); + + return $this; + } + + public function removeExpired() + { + $currentTime = time(); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { + return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); + }); + + return $this; + } + + public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) + { + return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( + $domain, + $path, + $name, + $skipDiscardable, + $skipExpired + ) { + return false === (($name && $cookie->getName() != $name) || + ($skipExpired && $cookie->isExpired()) || + ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || + ($path && !$cookie->matchesPath($path)) || + ($domain && !$cookie->matchesDomain($domain))); + })); + } + + public function add(Cookie $cookie) + { + // Only allow cookies with set and valid domain, name, value + $result = $cookie->validate(); + if ($result !== true) { + if ($this->strictMode) { + throw new InvalidCookieException($result); + } else { + $this->removeCookieIfEmpty($cookie); + return false; + } + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Two cookies are identical, when their path, domain, port and name are identical + if ($c->getPath() != $cookie->getPath() || + $c->getDomain() != $cookie->getDomain() || + $c->getPorts() != $cookie->getPorts() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + /** + * Serializes the cookie cookieJar + * + * @return string + */ + public function serialize() + { + // Only serialize long term cookies and unexpired cookies + return json_encode(array_map(function (Cookie $cookie) { + return $cookie->toArray(); + }, $this->all(null, null, null, true, true))); + } + + /** + * Unserializes the cookie cookieJar + */ + public function unserialize($data) + { + $data = json_decode($data, true); + if (empty($data)) { + $this->cookies = array(); + } else { + $this->cookies = array_map(function (array $cookie) { + return new Cookie($cookie); + }, $data); + } + } + + /** + * Returns the total number of stored cookies + * + * @return int + */ + public function count() + { + return count($this->cookies); + } + + /** + * Returns an iterator + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + public function addCookiesFromResponse(Response $response, RequestInterface $request = null) + { + if ($cookieHeader = $response->getHeader('Set-Cookie')) { + $parser = ParserRegistry::getInstance()->getParser('cookie'); + foreach ($cookieHeader as $cookie) { + if ($parsed = $request + ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) + : $parser->parseCookie($cookie) + ) { + // Break up cookie v2 into multiple cookies + foreach ($parsed['cookies'] as $key => $value) { + $row = $parsed; + $row['name'] = $key; + $row['value'] = $value; + unset($row['cookies']); + $this->add(new Cookie($row)); + } + } + } + } + } + + public function getMatchingCookies(RequestInterface $request) + { + // Find cookies that match this request + $cookies = $this->all($request->getHost(), $request->getPath()); + // Remove ineligible cookies + foreach ($cookies as $index => $cookie) { + if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { + unset($cookies[$index]); + } + }; + + return $cookies; + } + + /** + * If a cookie already exists and the server asks to set it again with a null value, the + * cookie must be deleted. + * + * @param \Guzzle\Plugin\Cookie\Cookie $cookie + */ + private function removeCookieIfEmpty(Cookie $cookie) + { + $cookieValue = $cookie->getValue(); + if ($cookieValue === null || $cookieValue === '') { + $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php new file mode 100644 index 000000000..7faa7d21f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php @@ -0,0 +1,85 @@ +filename = $cookieFile; + $this->load(); + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->persist(); + } + + /** + * Save the contents of the data array to the file + * + * @throws RuntimeException if the file cannot be found or created + */ + protected function persist() + { + if (false === file_put_contents($this->filename, $this->serialize())) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load the contents of the json formatted file into the data array and discard any unsaved state + */ + protected function load() + { + $json = file_get_contents($this->filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + + $this->unserialize($json); + $this->cookies = $this->cookies ?: array(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php new file mode 100644 index 000000000..df3210ee1 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php @@ -0,0 +1,70 @@ +cookieJar = $cookieJar ?: new ArrayCookieJar(); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', 125), + 'request.sent' => array('onRequestSent', 125) + ); + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Add cookies before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + if (!$request->getParams()->get('cookies.disable')) { + $request->removeHeader('Cookie'); + // Find cookies that match this request + foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { + $request->addCookie($cookie->getName(), $cookie->getValue()); + } + } + } + + /** + * Extract cookies from a sent request + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php new file mode 100644 index 000000000..b1fa6fd89 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Cookie": "" } + }, + "target-dir": "Guzzle/Plugin/Cookie", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php new file mode 100644 index 000000000..610e60cad --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php @@ -0,0 +1,46 @@ +getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest'); + */ +class CurlAuthPlugin implements EventSubscriberInterface +{ + private $username; + private $password; + private $scheme; + + /** + * @param string $username HTTP basic auth username + * @param string $password Password + * @param int $scheme Curl auth scheme + */ + public function __construct($username, $password, $scheme=CURLAUTH_BASIC) + { + Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');"); + $this->username = $username; + $this->password = $password; + $this->scheme = $scheme; + } + + public static function getSubscribedEvents() + { + return array('client.create_request' => array('onRequestCreate', 255)); + } + + /** + * Add basic auth + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + $event['request']->setAuth($this->username, $this->password, $this->scheme); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json new file mode 100644 index 000000000..edc8b24e5 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-curlauth", + "description": "Guzzle cURL authorization plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "curl", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" } + }, + "target-dir": "Guzzle/Plugin/CurlAuth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php new file mode 100644 index 000000000..5dce8bd6c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php @@ -0,0 +1,22 @@ + array('onCommandBeforeSend', -1)); + } + + /** + * Adds a listener to requests before they sent from a command + * + * @param Event $event Event emitted + */ + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + if ($operation = $command->getOperation()) { + if ($operation->getErrorResponses()) { + $request = $command->getRequest(); + $request->getEventDispatcher() + ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation)); + } + } + } + + /** + * @param RequestInterface $request Request that received an error + * @param CommandInterface $command Command that created the request + * @param Operation $operation Operation that defines the request and errors + * + * @return \Closure Returns a closure + * @throws ErrorResponseException + */ + protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation) + { + return function (Event $event) use ($request, $command, $operation) { + $response = $event['response']; + foreach ($operation->getErrorResponses() as $error) { + if (!isset($error['class'])) { + continue; + } + if (isset($error['code']) && $response->getStatusCode() != $error['code']) { + continue; + } + if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) { + continue; + } + $className = $error['class']; + $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface'; + if (!class_exists($className)) { + throw new ErrorResponseException("{$className} does not exist"); + } elseif (!(in_array($errorClassInterface, class_implements($className)))) { + throw new ErrorResponseException("{$className} must implement {$errorClassInterface}"); + } + throw $className::fromCommand($command, $response); + } + }; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php new file mode 100644 index 000000000..1d89e40e7 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php @@ -0,0 +1,7 @@ +=5.3.2", + "guzzle/service": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" } + }, + "target-dir": "Guzzle/Plugin/ErrorResponse", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php new file mode 100644 index 000000000..7375e892b --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php @@ -0,0 +1,163 @@ + array('onRequestSent', 9999)); + } + + /** + * Convert to a string that contains all request and response headers + * + * @return string + */ + public function __toString() + { + $lines = array(); + foreach ($this->transactions as $entry) { + $response = isset($entry['response']) ? $entry['response'] : ''; + $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; + } + + return implode("\n", $lines); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * @param Response $response Response of the request + * + * @return HistoryPlugin + */ + public function add(RequestInterface $request, Response $response = null) + { + if (!$response && $request->getResponse()) { + $response = $request->getResponse(); + } + + $this->transactions[] = array('request' => $request, 'response' => $response); + if (count($this->transactions) > $this->getlimit()) { + array_shift($this->transactions); + } + + return $this; + } + + /** + * Set the max number of requests to store + * + * @param int $limit Limit + * + * @return HistoryPlugin + */ + public function setLimit($limit) + { + $this->limit = (int) $limit; + + return $this; + } + + /** + * Get the request limit + * + * @return int + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Get all of the raw transactions in the form of an array of associative arrays containing + * 'request' and 'response' keys. + * + * @return array + */ + public function getAll() + { + return $this->transactions; + } + + /** + * Get the requests in the history + * + * @return \ArrayIterator + */ + public function getIterator() + { + // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll()) + return new \ArrayIterator(array_map(function ($entry) { + $entry['request']->getParams()->set('actual_response', $entry['response']); + return $entry['request']; + }, $this->transactions)); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->transactions); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + $last = end($this->transactions); + + return $last['request']; + } + + /** + * Get the last response in the history + * + * @return Response|null + */ + public function getLastResponse() + { + $last = end($this->transactions); + + return isset($last['response']) ? $last['response'] : null; + } + + /** + * Clears the history + * + * @return HistoryPlugin + */ + public function clear() + { + $this->transactions = array(); + + return $this; + } + + public function onRequestSent(Event $event) + { + $this->add($event['request'], $event['response']); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json new file mode 100644 index 000000000..ba0bf2c4d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-history", + "description": "Guzzle history plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\History": "" } + }, + "target-dir": "Guzzle/Plugin/History", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php new file mode 100644 index 000000000..cabdea854 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php @@ -0,0 +1,161 @@ +logAdapter = $logAdapter; + $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter); + $this->wireBodies = $wireBodies; + } + + /** + * Get a log plugin that outputs full request, response, and curl error information to stderr + * + * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable + * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available + * + * @return self + */ + public static function getDebugPlugin($wireBodies = true, $stream = null) + { + if ($stream === null) { + if (defined('STDERR')) { + $stream = STDERR; + } else { + $stream = fopen('php://output', 'w'); + } + } + + return new self(new ClosureLogAdapter(function ($m) use ($stream) { + fwrite($stream, $m . PHP_EOL); + }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies); + } + + public static function getSubscribedEvents() + { + return array( + 'curl.callback.write' => array('onCurlWrite', 255), + 'curl.callback.read' => array('onCurlRead', 255), + 'request.before_send' => array('onRequestBeforeSend', 255), + 'request.sent' => array('onRequestSent', 255) + ); + } + + /** + * Event triggered when curl data is read from a request + * + * @param Event $event + */ + public function onCurlRead(Event $event) + { + // Stream the request body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('request_wire')) { + $wire->write($event['read']); + } + } + + /** + * Event triggered when curl data is written to a response + * + * @param Event $event + */ + public function onCurlWrite(Event $event) + { + // Stream the response body to the log if the body is not repeatable + if ($wire = $event['request']->getParams()->get('response_wire')) { + $wire->write($event['write']); + } + } + + /** + * Called before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + if ($this->wireBodies) { + $request = $event['request']; + // Ensure that curl IO events are emitted + $request->getCurlOptions()->set('emit_io', true); + // We need to make special handling for content wiring and non-repeatable streams. + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable()) + ) { + // The body of the request cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('request_wire', EntityBody::factory()); + } + if (!$request->getResponseBody()->isRepeatable()) { + // The body of the response cannot be recalled so logging the body will require us to buffer it + $request->getParams()->set('response_wire', EntityBody::factory()); + } + } + } + + /** + * Triggers the actual log write when a request completes + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $handle = $event['handle']; + + if ($wire = $request->getParams()->get('request_wire')) { + $request = clone $request; + $request->setBody($wire); + } + + if ($wire = $request->getParams()->get('response_wire')) { + $response = clone $response; + $response->setBody($wire); + } + + // Send the log message to the adapter, adding a category and host + $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG; + $message = $this->formatter->format($request, $response, $handle); + $this->logAdapter->log($message, $priority, array( + 'request' => $request, + 'response' => $response, + 'handle' => $handle + )); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json new file mode 100644 index 000000000..130e6da0a --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json @@ -0,0 +1,28 @@ +{ + "name": "guzzle/plugin-log", + "description": "Guzzle log plugin for over the wire logging", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "log", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Log": "" } + }, + "target-dir": "Guzzle/Plugin/Log", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php new file mode 100644 index 000000000..851242433 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php @@ -0,0 +1,57 @@ +contentMd5Param = $contentMd5Param; + $this->validateMd5Param = $validateMd5Param; + } + + public static function getSubscribedEvents() + { + return array('command.before_send' => array('onCommandBeforeSend', -255)); + } + + public function onCommandBeforeSend(Event $event) + { + $command = $event['command']; + $request = $command->getRequest(); + + // Only add an MD5 is there is a MD5 option on the operation and it has a payload + if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() + && $command->getOperation()->hasParam($this->contentMd5Param)) { + // Check if an MD5 checksum value should be passed along to the request + if ($command[$this->contentMd5Param] === true) { + if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) { + $request->setHeader('Content-MD5', $md5); + } + } + } + + // Check if MD5 validation should be used with the response + if ($command[$this->validateMd5Param] === true) { + $request->addSubscriber(new Md5ValidatorPlugin(true, false)); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php new file mode 100644 index 000000000..5d7a3785e --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php @@ -0,0 +1,88 @@ +contentLengthCutoff = $contentLengthCutoff; + $this->contentEncoded = $contentEncoded; + } + + public static function getSubscribedEvents() + { + return array('request.complete' => array('onRequestComplete', 255)); + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException + */ + public function onRequestComplete(Event $event) + { + $response = $event['response']; + + if (!$contentMd5 = $response->getContentMd5()) { + return; + } + + $contentEncoding = $response->getContentEncoding(); + if ($contentEncoding && !$this->contentEncoded) { + return false; + } + + // Make sure that the size of the request is under the cutoff size + if ($this->contentLengthCutoff) { + $size = $response->getContentLength() ?: $response->getBody()->getSize(); + if (!$size || $size > $this->contentLengthCutoff) { + return; + } + } + + if (!$contentEncoding) { + $hash = $response->getBody()->getContentMd5(); + } elseif ($contentEncoding == 'gzip') { + $response->getBody()->compress('zlib.deflate'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } elseif ($contentEncoding == 'compress') { + $response->getBody()->compress('bzip2.compress'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } else { + return; + } + + if ($contentMd5 !== $hash) { + throw new UnexpectedValueException( + "The response entity body may have been modified over the wire. The Content-MD5 " + . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})." + ); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json new file mode 100644 index 000000000..0602d0609 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-md5", + "description": "Guzzle MD5 plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Md5": "" } + }, + "target-dir": "Guzzle/Plugin/Md5", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php new file mode 100644 index 000000000..2440578cf --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php @@ -0,0 +1,245 @@ +readBodies = $readBodies; + $this->temporary = $temporary; + if ($items) { + foreach ($items as $item) { + if ($item instanceof \Exception) { + $this->addException($item); + } else { + $this->addResponse($item); + } + } + } + } + + public static function getSubscribedEvents() + { + // Use a number lower than the CachePlugin + return array('request.before_send' => array('onRequestBeforeSend', -999)); + } + + public static function getAllEvents() + { + return array('mock.request'); + } + + /** + * Get a mock response from a file + * + * @param string $path File to retrieve a mock response from + * + * @return Response + * @throws InvalidArgumentException if the file is not found + */ + public static function getMockFile($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentException('Unable to open mock file: ' . $path); + } + + return Response::fromMessage(file_get_contents($path)); + } + + /** + * Set whether or not to consume the entity body of a request when a mock + * response is used + * + * @param bool $readBodies Set to true to read and consume entity bodies + * + * @return self + */ + public function readBodies($readBodies) + { + $this->readBodies = $readBodies; + + return $this; + } + + /** + * Returns the number of remaining mock responses + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|Response $response Response object or path to response file + * + * @return MockPlugin + * @throws InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (!($response instanceof Response)) { + if (!is_string($response)) { + throw new InvalidArgumentException('Invalid response'); + } + $response = self::getMockFile($response); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Add an exception to the end of the queue + * + * @param CurlException $e Exception to throw when the request is executed + * + * @return MockPlugin + */ + public function addException(CurlException $e) + { + $this->queue[] = $e; + + return $this; + } + + /** + * Clear the queue + * + * @return MockPlugin + */ + public function clearQueue() + { + $this->queue = array(); + + return $this; + } + + /** + * Returns an array of mock responses remaining in the queue + * + * @return array + */ + public function getQueue() + { + return $this->queue; + } + + /** + * Check if this is a temporary plugin + * + * @return bool + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Get a response from the front of the list and add it to a request + * + * @param RequestInterface $request Request to mock + * + * @return self + * @throws CurlException When request.send is called and an exception is queued + */ + public function dequeue(RequestInterface $request) + { + $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request)); + + $item = array_shift($this->queue); + if ($item instanceof Response) { + if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) { + $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) { + while ($data = $event['request']->getBody()->read(8096)); + // Remove the listener after one-time use + $event['request']->getEventDispatcher()->removeListener('request.sent', $f); + }); + } + $request->setResponse($item); + } elseif ($item instanceof CurlException) { + // Emulates exceptions encountered while transferring requests + $item->setRequest($request); + $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item)); + // Only throw if the exception wasn't handled + if ($state == RequestInterface::STATE_ERROR) { + throw $item; + } + } + + return $this; + } + + /** + * Clear the array of received requests + */ + public function flush() + { + $this->received = array(); + } + + /** + * Get an array of requests that were mocked by this plugin + * + * @return array + */ + public function getReceivedRequests() + { + return $this->received; + } + + /** + * Called when a request is about to be sent + * + * @param Event $event + * @throws \OutOfBoundsException When queue is empty + */ + public function onRequestBeforeSend(Event $event) + { + if (!$this->queue) { + throw new \OutOfBoundsException('Mock queue is empty'); + } + + $request = $event['request']; + $this->received[] = $request; + // Detach the filter from the client so it's a one-time use + if ($this->temporary && count($this->queue) == 1 && $request->getClient()) { + $request->getClient()->getEventDispatcher()->removeSubscriber($this); + } + $this->dequeue($request); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json new file mode 100644 index 000000000..f8201e31f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-mock", + "description": "Guzzle Mock plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["mock", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Mock": "" } + }, + "target-dir": "Guzzle/Plugin/Mock", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php new file mode 100644 index 000000000..95e0c3e4a --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php @@ -0,0 +1,306 @@ +config = Collection::fromConfig($config, array( + 'version' => '1.0', + 'request_method' => self::REQUEST_METHOD_HEADER, + 'consumer_key' => 'anonymous', + 'consumer_secret' => 'anonymous', + 'signature_method' => 'HMAC-SHA1', + 'signature_callback' => function($stringToSign, $key) { + return hash_hmac('sha1', $stringToSign, $key, true); + } + ), array( + 'signature_method', 'signature_callback', 'version', + 'consumer_key', 'consumer_secret' + )); + } + + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -1000) + ); + } + + /** + * Request before-send event handler + * + * @param Event $event Event received + * @return array + * @throws \InvalidArgumentException + */ + public function onRequestBeforeSend(Event $event) + { + $timestamp = $this->getTimestamp($event); + $request = $event['request']; + $nonce = $this->generateNonce($request); + $authorizationParams = $this->getOauthParams($timestamp, $nonce); + $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce); + + switch ($this->config['request_method']) { + case self::REQUEST_METHOD_HEADER: + $request->setHeader( + 'Authorization', + $this->buildAuthorizationHeader($authorizationParams) + ); + break; + case self::REQUEST_METHOD_QUERY: + foreach ($authorizationParams as $key => $value) { + $request->getQuery()->set($key, $value); + } + break; + default: + throw new \InvalidArgumentException(sprintf( + 'Invalid consumer method "%s"', + $this->config['request_method'] + )); + } + + return $authorizationParams; + } + + /** + * Builds the Authorization header for a request + * + * @param array $authorizationParams Associative array of authorization parameters + * + * @return string + */ + private function buildAuthorizationHeader($authorizationParams) + { + $authorizationString = 'OAuth '; + foreach ($authorizationParams as $key => $val) { + if ($val) { + $authorizationString .= $key . '="' . urlencode($val) . '", '; + } + } + + return substr($authorizationString, 0, -2); + } + + /** + * Calculate signature for request + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getSignature(RequestInterface $request, $timestamp, $nonce) + { + $string = $this->getStringToSign($request, $timestamp, $nonce); + $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); + + return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); + } + + /** + * Calculate string to sign + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return string + */ + public function getStringToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getParamsToSign($request, $timestamp, $nonce); + + // Convert booleans to strings. + $params = $this->prepareParameters($params); + + // Build signing string from combined params + $parameterString = clone $request->getQuery(); + $parameterString->replace($params); + + $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); + + return strtoupper($request->getMethod()) . '&' + . rawurlencode($url) . '&' + . rawurlencode((string) $parameterString); + } + + /** + * Get the oauth parameters as named by the oauth spec + * + * @param $timestamp + * @param $nonce + * @return Collection + */ + protected function getOauthParams($timestamp, $nonce) + { + $params = new Collection(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $nonce, + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + )); + + // Optional parameters should not be set if they have not been set in the config as + // the parameter may be considered invalid by the Oauth service. + $optionalParams = array( + 'callback' => 'oauth_callback', + 'token' => 'oauth_token', + 'verifier' => 'oauth_verifier', + 'version' => 'oauth_version' + ); + + foreach ($optionalParams as $optionName => $oauthName) { + if (isset($this->config[$optionName]) == true) { + $params[$oauthName] = $this->config[$optionName]; + } + } + + return $params; + } + + /** + * Get all of the parameters required to sign a request including: + * * The oauth params + * * The request GET params + * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded) + * + * @param RequestInterface $request Request to generate a signature for + * @param integer $timestamp Timestamp to use for nonce + * @param string $nonce + * + * @return array + */ + public function getParamsToSign(RequestInterface $request, $timestamp, $nonce) + { + $params = $this->getOauthParams($timestamp, $nonce); + + // Add query string parameters + $params->merge($request->getQuery()); + + // Add POST fields to signing string if required + if ($this->shouldPostFieldsBeSigned($request)) + { + $params->merge($request->getPostFields()); + } + + // Sort params + $params = $params->toArray(); + uksort($params, 'strcmp'); + + return $params; + } + + /** + * Decide whether the post fields should be added to the base string that Oauth signs. + * This implementation is correct. Non-conformant APIs may require that this method be + * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type + * is 'application/x-www-form-urlencoded' + * + * @param $request + * @return bool Whether the post fields should be signed or not + */ + public function shouldPostFieldsBeSigned($request) + { + if (!$this->config->get('disable_post_params') && + $request instanceof EntityEnclosingRequestInterface && + false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) + { + return true; + } + + return false; + } + + /** + * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same + * exact timestamp to use separate nonce's. + * + * @param RequestInterface $request Request to generate a nonce for + * + * @return string + */ + public function generateNonce(RequestInterface $request) + { + return sha1(uniqid('', true) . $request->getUrl()); + } + + /** + * Gets timestamp from event or create new timestamp + * + * @param Event $event Event containing contextual information + * + * @return int + */ + public function getTimestamp(Event $event) + { + return $event['timestamp'] ?: time(); + } + + /** + * Convert booleans to strings, removed unset parameters, and sorts the array + * + * @param array $data Data array + * + * @return array + */ + protected function prepareParameters($data) + { + ksort($data); + foreach ($data as $key => &$value) { + switch (gettype($value)) { + case 'NULL': + unset($data[$key]); + break; + case 'array': + $data[$key] = self::prepareParameters($value); + break; + case 'boolean': + $data[$key] = $value ? 'true' : 'false'; + break; + } + } + + return $data; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json new file mode 100644 index 000000000..c9766ba16 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json @@ -0,0 +1,27 @@ +{ + "name": "guzzle/plugin-oauth", + "description": "Guzzle OAuth plugin", + "homepage": "http://guzzlephp.org/", + "keywords": ["oauth", "plugin", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin\\Oauth": "" } + }, + "target-dir": "Guzzle/Plugin/Oauth", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json new file mode 100644 index 000000000..2bbe64cc5 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json @@ -0,0 +1,44 @@ +{ + "name": "guzzle/plugin", + "description": "Guzzle plugin component containing all Guzzle HTTP plugins", + "homepage": "http://guzzlephp.org/", + "keywords": ["http", "client", "plugin", "extension", "guzzle"], + "license": "MIT", + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "require": { + "php": ">=5.3.2", + "guzzle/http": "self.version" + }, + "suggest": { + "guzzle/cache": "self.version", + "guzzle/log": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Plugin": "" } + }, + "target-dir": "Guzzle/Plugin", + "replace": { + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version" + }, + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php new file mode 100644 index 000000000..cd06f5722 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php @@ -0,0 +1,177 @@ + 'JSON_ERROR_NONE - No errors', + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' + ); + + public function load($config, array $options = array()) + { + // Reset the array of loaded files because this is a new config + $this->loadedFiles = array(); + + if (is_string($config)) { + $config = $this->loadFile($config); + } elseif (!is_array($config)) { + throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); + } else { + $this->mergeIncludes($config); + } + + return $this->build($config, $options); + } + + /** + * Add an include alias to the loader + * + * @param string $filename Filename to alias (e.g. _foo) + * @param string $alias Actual file to use (e.g. /path/to/foo.json) + * + * @return self + */ + public function addAlias($filename, $alias) + { + $this->aliases[$filename] = $alias; + + return $this; + } + + /** + * Remove an alias from the loader + * + * @param string $alias Alias to remove + * + * @return self + */ + public function removeAlias($alias) + { + unset($this->aliases[$alias]); + + return $this; + } + + /** + * Perform the parsing of a config file and create the end result + * + * @param array $config Configuration data + * @param array $options Options to use when building + * + * @return mixed + */ + protected abstract function build($config, array $options); + + /** + * Load a configuration file (can load JSON or PHP files that return an array when included) + * + * @param string $filename File to load + * + * @return array + * @throws InvalidArgumentException + * @throws RuntimeException when the JSON cannot be parsed + */ + protected function loadFile($filename) + { + if (isset($this->aliases[$filename])) { + $filename = $this->aliases[$filename]; + } + + switch (pathinfo($filename, PATHINFO_EXTENSION)) { + case 'js': + case 'json': + $level = error_reporting(0); + $json = file_get_contents($filename); + error_reporting($level); + + if ($json === false) { + $err = error_get_last(); + throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']); + } + + $config = json_decode($json, true); + // Throw an exception if there was an error loading the file + if ($error = json_last_error()) { + $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error'; + throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}"); + } + break; + case 'php': + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + $config = require $filename; + if (!is_array($config)) { + throw new InvalidArgumentException('PHP files must return an array of configuration data'); + } + break; + default: + throw new InvalidArgumentException('Unknown file extension: ' . $filename); + } + + // Keep track of this file being loaded to prevent infinite recursion + $this->loadedFiles[$filename] = true; + + // Merge include files into the configuration array + $this->mergeIncludes($config, dirname($filename)); + + return $config; + } + + /** + * Merges in all include files + * + * @param array $config Config data that contains includes + * @param string $basePath Base path to use when a relative path is encountered + * + * @return array Returns the merged and included data + */ + protected function mergeIncludes(&$config, $basePath = null) + { + if (!empty($config['includes'])) { + foreach ($config['includes'] as &$path) { + // Account for relative paths + if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { + $path = "{$basePath}/{$path}"; + } + // Don't load the same files more than once + if (!isset($this->loadedFiles[$path])) { + $this->loadedFiles[$path] = true; + $config = $this->mergeData($this->loadFile($path), $config); + } + } + } + } + + /** + * Default implementation for merging two arrays of data (uses array_merge_recursive) + * + * @param array $a Original data + * @param array $b Data to merge into the original and overwrite existing values + * + * @return array + */ + protected function mergeData(array $a, array $b) + { + return array_merge_recursive($a, $b); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php new file mode 100644 index 000000000..38150db4b --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -0,0 +1,189 @@ +load($config, $globalParameters); + } + + /** + * @param array $serviceBuilderConfig Service configuration settings: + * - name: Name of the service + * - class: Client class to instantiate using a factory method + * - params: array of key value pair configuration settings for the builder + */ + public function __construct(array $serviceBuilderConfig = array()) + { + $this->builderConfig = $serviceBuilderConfig; + } + + public static function getAllEvents() + { + return array('service_builder.create_client'); + } + + public function unserialize($serialized) + { + $this->builderConfig = json_decode($serialized, true); + } + + public function serialize() + { + return json_encode($this->builderConfig); + } + + /** + * Attach a plugin to every client created by the builder + * + * @param EventSubscriberInterface $plugin Plugin to attach to each client + * + * @return self + */ + public function addGlobalPlugin(EventSubscriberInterface $plugin) + { + $this->plugins[] = $plugin; + + return $this; + } + + /** + * Get data from the service builder without triggering the building of a service + * + * @param string $name Name of the service to retrieve + * + * @return array|null + */ + public function getData($name) + { + return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null; + } + + public function get($name, $throwAway = false) + { + if (!isset($this->builderConfig[$name])) { + + // Check to see if arbitrary data is being referenced + if (isset($this->clients[$name])) { + return $this->clients[$name]; + } + + // Check aliases and return a match if found + foreach ($this->builderConfig as $actualName => $config) { + if (isset($config['alias']) && $config['alias'] == $name) { + return $this->get($actualName, $throwAway); + } + } + throw new ServiceNotFoundException('No service is registered as ' . $name); + } + + if (!$throwAway && isset($this->clients[$name])) { + return $this->clients[$name]; + } + + $builder =& $this->builderConfig[$name]; + + // Convert references to the actual client + foreach ($builder['params'] as &$v) { + if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') { + $v = $this->get(trim($v, '{} ')); + } + } + + // Get the configured parameters and merge in any parameters provided for throw-away clients + $config = $builder['params']; + if (is_array($throwAway)) { + $config = $throwAway + $config; + } + + $client = $builder['class']::factory($config); + + if (!$throwAway) { + $this->clients[$name] = $client; + } + + if ($client instanceof ClientInterface) { + foreach ($this->plugins as $plugin) { + $client->addSubscriber($plugin); + } + // Dispatch an event letting listeners know a client was created + $this->dispatch('service_builder.create_client', array('client' => $client)); + } + + return $client; + } + + public function set($key, $service) + { + if (is_array($service) && isset($service['class']) && isset($service['params'])) { + $this->builderConfig[$key] = $service; + } else { + $this->clients[$key] = $service; + } + + return $this; + } + + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + public function offsetUnset($offset) + { + unset($this->builderConfig[$offset]); + unset($this->clients[$offset]); + } + + public function offsetExists($offset) + { + return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]); + } + + public function offsetGet($offset) + { + return $this->get($offset); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php new file mode 100644 index 000000000..4fc310a47 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php @@ -0,0 +1,40 @@ + &$service) { + + $service['params'] = isset($service['params']) ? $service['params'] : array(); + + // Check if this client builder extends another client + if (!empty($service['extends'])) { + + // Make sure that the service it's extending has been defined + if (!isset($services[$service['extends']])) { + throw new ServiceNotFoundException( + "{$name} is trying to extend a non-existent service: {$service['extends']}" + ); + } + + $extended = &$services[$service['extends']]; + + // Use the correct class attribute + if (empty($service['class'])) { + $service['class'] = isset($extended['class']) ? $extended['class'] : ''; + } + if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) { + $service['params'] = $service['params'] + $extendsParams; + } + } + + // Overwrite default values with global parameter values + if (!empty($options)) { + $service['params'] = $options + $service['params']; + } + + $service['class'] = isset($service['class']) ? $service['class'] : ''; + } + + return new $class($services); + } + + protected function mergeData(array $a, array $b) + { + $result = $b + $a; + + // Merge services using a recursive union of arrays + if (isset($a['services']) && $b['services']) { + + // Get a union of the services of the two arrays + $result['services'] = $b['services'] + $a['services']; + + // Merge each service in using a union of the two arrays + foreach ($result['services'] as $name => &$service) { + + // By default, services completely override a previously defined service unless it extends itself + if (isset($a['services'][$name]['extends']) + && isset($b['services'][$name]['extends']) + && $b['services'][$name]['extends'] == $name + ) { + $service += $a['services'][$name]; + // Use the `extends` attribute of the parent + $service['extends'] = $a['services'][$name]['extends']; + // Merge parameters using a union if both have parameters + if (isset($a['services'][$name]['params'])) { + $service['params'] += $a['services'][$name]['params']; + } + } + } + } + + return $result; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php new file mode 100644 index 000000000..26f8360cc --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php @@ -0,0 +1,46 @@ +loader = $loader; + $this->cache = $cache; + } + + public function load($config, array $options = array()) + { + if (!is_string($config)) { + $key = false; + } else { + $key = 'loader_' . crc32($config); + if ($result = $this->cache->fetch($key)) { + return $result; + } + } + + $result = $this->loader->load($config, $options); + if ($key) { + $this->cache->save($key, $result); + } + + return $result; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php new file mode 100644 index 000000000..3e5f8e53d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php @@ -0,0 +1,297 @@ +getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); + } + + public function getCommand($name, array $args = array()) + { + // Add global client options to the command + if ($options = $this->getConfig(self::COMMAND_PARAMS)) { + $args += $options; + } + + if (!($command = $this->getCommandFactory()->factory($name, $args))) { + throw new InvalidArgumentException("Command was not found matching {$name}"); + } + + $command->setClient($this); + $this->dispatch('client.command.create', array('client' => $this, 'command' => $command)); + + return $command; + } + + /** + * Set the command factory used to create commands by name + * + * @param CommandFactoryInterface $factory Command factory + * + * @return self + */ + public function setCommandFactory(CommandFactoryInterface $factory) + { + $this->commandFactory = $factory; + + return $this; + } + + /** + * Set the resource iterator factory associated with the client + * + * @param ResourceIteratorFactoryInterface $factory Resource iterator factory + * + * @return self + */ + public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) + { + $this->resourceIteratorFactory = $factory; + + return $this; + } + + public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) + { + if (!($command instanceof CommandInterface)) { + $command = $this->getCommand($command, $commandOptions ?: array()); + } + + return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); + } + + public function execute($command) + { + if ($command instanceof CommandInterface) { + $this->send($this->prepareCommand($command)); + $this->dispatch('command.after_send', array('command' => $command)); + return $command->getResult(); + } elseif (is_array($command) || $command instanceof \Traversable) { + return $this->executeMultiple($command); + } else { + throw new InvalidArgumentException('Command must be a command or array of commands'); + } + } + + public function setDescription(ServiceDescriptionInterface $service) + { + $this->serviceDescription = $service; + + if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) { + $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service)); + } + + // If a baseUrl was set on the description, then update the client + if ($baseUrl = $service->getBaseUrl()) { + $this->setBaseUrl($baseUrl); + } + + return $this; + } + + public function getDescription() + { + return $this->serviceDescription; + } + + /** + * Set the inflector used with the client + * + * @param InflectorInterface $inflector Inflection object + * + * @return self + */ + public function setInflector(InflectorInterface $inflector) + { + $this->inflector = $inflector; + + return $this; + } + + /** + * Get the inflector used with the client + * + * @return self + */ + public function getInflector() + { + if (!$this->inflector) { + $this->inflector = Inflector::getDefault(); + } + + return $this->inflector; + } + + /** + * Prepare a command for sending and get the RequestInterface object created by the command + * + * @param CommandInterface $command Command to prepare + * + * @return RequestInterface + */ + protected function prepareCommand(CommandInterface $command) + { + // Set the client and prepare the command + $request = $command->setClient($this)->prepare(); + // Set the state to new if the command was previously executed + $request->setState(RequestInterface::STATE_NEW); + $this->dispatch('command.before_send', array('command' => $command)); + + return $request; + } + + /** + * Execute multiple commands in parallel + * + * @param array|Traversable $commands Array of CommandInterface objects to execute + * + * @return array Returns an array of the executed commands + * @throws Exception\CommandTransferException + */ + protected function executeMultiple($commands) + { + $requests = array(); + $commandRequests = new \SplObjectStorage(); + + foreach ($commands as $command) { + $request = $this->prepareCommand($command); + $commandRequests[$request] = $command; + $requests[] = $request; + } + + try { + $this->send($requests); + foreach ($commands as $command) { + $this->dispatch('command.after_send', array('command' => $command)); + } + return $commands; + } catch (MultiTransferException $failureException) { + // Throw a CommandTransferException using the successful and failed commands + $e = CommandTransferException::fromMultiTransferException($failureException); + + // Remove failed requests from the successful requests array and add to the failures array + foreach ($failureException->getFailedRequests() as $request) { + if (isset($commandRequests[$request])) { + $e->addFailedCommand($commandRequests[$request]); + unset($commandRequests[$request]); + } + } + + // Always emit the command after_send events for successful commands + foreach ($commandRequests as $success) { + $e->addSuccessfulCommand($commandRequests[$success]); + $this->dispatch('command.after_send', array('command' => $commandRequests[$success])); + } + + throw $e; + } + } + + protected function getResourceIteratorFactory() + { + if (!$this->resourceIteratorFactory) { + // Build the default resource iterator factory if one is not set + $clientClass = get_class($this); + $prefix = substr($clientClass, 0, strrpos($clientClass, '\\')); + $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array( + "{$prefix}\\Iterator", + "{$prefix}\\Model" + )); + } + + return $this->resourceIteratorFactory; + } + + /** + * Get the command factory associated with the client + * + * @return CommandFactoryInterface + */ + protected function getCommandFactory() + { + if (!$this->commandFactory) { + $this->commandFactory = CompositeFactory::getDefaultChain($this); + } + + return $this->commandFactory; + } + + /** + * @deprecated + * @codeCoverageIgnore + */ + public function enableMagicMethods($isEnabled) + { + Version::warn(__METHOD__ . ' is deprecated'); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php new file mode 100644 index 000000000..814154f00 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php @@ -0,0 +1,68 @@ +operation = $operation ?: $this->createOperation(); + foreach ($this->operation->getParams() as $name => $arg) { + $currentValue = $this[$name]; + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be updated on the config object + if ($currentValue !== $configValue) { + $this[$name] = $configValue; + } + } + + $headers = $this[self::HEADERS_OPTION]; + if (!$headers instanceof Collection) { + $this[self::HEADERS_OPTION] = new Collection((array) $headers); + } + + // You can set a command.on_complete option in your parameters to set an onComplete callback + if ($onComplete = $this['command.on_complete']) { + unset($this['command.on_complete']); + $this->setOnComplete($onComplete); + } + + // Set the hidden additional parameters + if (!$this[self::HIDDEN_PARAMS]) { + $this[self::HIDDEN_PARAMS] = array( + self::HEADERS_OPTION, + self::RESPONSE_PROCESSING, + self::HIDDEN_PARAMS, + self::REQUEST_OPTIONS + ); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Execute the command in the same manner as calling a function + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + */ + public function __invoke() + { + return $this->execute(); + } + + public function getName() + { + return $this->operation->getName(); + } + + /** + * Get the API command information about the command + * + * @return OperationInterface + */ + public function getOperation() + { + return $this->operation; + } + + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + public function execute() + { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be executed.'); + } + + return $this->client->execute($this); + } + + public function getClient() + { + return $this->client; + } + + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + public function getResponse() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + return $this->request->getResponse(); + } + + public function getResult() + { + if (!$this->isExecuted()) { + $this->execute(); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + public function isPrepared() + { + return $this->request !== null; + } + + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A client must be associated with the command before it can be prepared.'); + } + + // If no response processing value was specified, then attempt to use the highest level of processing + if (!isset($this[self::RESPONSE_PROCESSING])) { + $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; + } + + // Notify subscribers of the client that the command is being prepared + $this->client->dispatch('command.before_prepare', array('command' => $this)); + + // Fail on missing required arguments, and change parameters via filters + $this->validate(); + // Delegate to the subclass that implements the build method + $this->build(); + + // Add custom request headers set on the command + if ($headers = $this[self::HEADERS_OPTION]) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + + // Add any curl options to the request + if ($options = $this[Client::CURL_OPTIONS]) { + $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); + } + + // Set a custom response body + if ($responseBody = $this[self::RESPONSE_BODY]) { + $this->request->setResponseBody($responseBody); + } + + $this->client->dispatch('command.after_prepare', array('command' => $this)); + } + + return $this->request; + } + + /** + * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is + * set, then the command will validate using the default {@see SchemaValidator}. + * + * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema + * + * @return self + */ + public function setValidator(ValidatorInterface $validator) + { + $this->validator = $validator; + + return $this; + } + + public function getRequestHeaders() + { + return $this[self::HEADERS_OPTION]; + } + + /** + * Initialize the command (hook that can be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Hook used to create an operation for concrete commands that are not associated with a service description + * + * @return OperationInterface + */ + protected function createOperation() + { + return new Operation(array('name' => get_class($this))); + } + + /** + * Create the result of the command after the request has been completed. + * Override this method in subclasses to customize this behavior + */ + protected function process() + { + $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW + ? DefaultResponseParser::getInstance()->parse($this) + : $this->request->getResponse(); + } + + /** + * Validate and prepare the command based on the schema and rules defined by the command's Operation object + * + * @throws ValidationException when validation errors occur + */ + protected function validate() + { + // Do not perform request validation/transformation if it is disable + if ($this[self::DISABLE_VALIDATION]) { + return; + } + + $errors = array(); + $validator = $this->getValidator(); + foreach ($this->operation->getParams() as $name => $schema) { + $value = $this[$name]; + if (!$validator->validate($schema, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + // Update the config value if it changed and no validation errors were encountered + $this->data[$name] = $value; + } + } + + // Validate additional parameters + $hidden = $this[self::HIDDEN_PARAMS]; + + if ($properties = $this->operation->getAdditionalParameters()) { + foreach ($this->toArray() as $name => $value) { + // It's only additional if it isn't defined in the schema + if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { + // Always set the name so that error messages are useful + $properties->setName($name); + if (!$validator->validate($properties, $value)) { + $errors = array_merge($errors, $validator->getErrors()); + } elseif ($value !== $this[$name]) { + $this->data[$name] = $value; + } + } + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } + + /** + * Get the validator used to prepare and validate properties. If no validator has been set on the command, then + * the default {@see SchemaValidator} will be used. + * + * @return ValidatorInterface + */ + protected function getValidator() + { + if (!$this->validator) { + $this->validator = SchemaValidator::getInstance(); + } + + return $this->validator; + } + + /** + * Get array of any validation errors + * If no validator has been set then return false + */ + public function getValidationErrors() + { + if (!$this->validator) { + return false; + } + + return $this->validator->getErrors(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100644 index 000000000..cb6ac40ce --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,41 @@ +request = $closure($this, $this->operation); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100644 index 000000000..fbb61d2ff --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,128 @@ +stopPropagation(); + } + + /** + * Get the created object + * + * @return mixed + */ + public function getResult() + { + return $this['result']; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php new file mode 100644 index 000000000..2dc4acd37 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php @@ -0,0 +1,169 @@ +factory = $factory; + } + + /** + * Add a location visitor to the serializer + * + * @param string $location Location to associate with the visitor + * @param RequestVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, RequestVisitorInterface $visitor) + { + $this->factory->addRequestVisitor($location, $visitor); + + return $this; + } + + public function prepare(CommandInterface $command) + { + $request = $this->createRequest($command); + // Keep an array of visitors found in the operation + $foundVisitors = array(); + $operation = $command->getOperation(); + + // Add arguments to the request using the location attribute + foreach ($operation->getParams() as $name => $arg) { + /** @var $arg \Guzzle\Service\Description\Parameter */ + $location = $arg->getLocation(); + // Skip 'uri' locations because they've already been processed + if ($location && $location != 'uri') { + // Instantiate visitors as they are detected in the properties + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getRequestVisitor($location); + } + // Ensure that a value has been set for this parameter + $value = $command[$name]; + if ($value !== null) { + // Apply the parameter value with the location visitor + $foundVisitors[$location]->visit($command, $request, $arg, $value); + } + } + } + + // Serialize additional parameters + if ($additional = $operation->getAdditionalParameters()) { + if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { + $foundVisitors[$additional->getLocation()] = $visitor; + } + } + + // Call the after method on each visitor found in the operation + foreach ($foundVisitors as $visitor) { + $visitor->after($command, $request); + } + + return $request; + } + + /** + * Serialize additional parameters + * + * @param OperationInterface $operation Operation that owns the command + * @param CommandInterface $command Command to prepare + * @param RequestInterface $request Request to serialize + * @param Parameter $additional Additional parameters + * + * @return null|RequestVisitorInterface + */ + protected function prepareAdditionalParameters( + OperationInterface $operation, + CommandInterface $command, + RequestInterface $request, + Parameter $additional + ) { + if (!($location = $additional->getLocation())) { + return; + } + + $visitor = $this->factory->getRequestVisitor($location); + $hidden = $command[$command::HIDDEN_PARAMS]; + + foreach ($command->toArray() as $key => $value) { + // Ignore values that are null or built-in command options + if ($value !== null + && !in_array($key, $hidden) + && !$operation->hasParam($key) + ) { + $additional->setName($key); + $visitor->visit($command, $request, $additional, $value); + } + } + + return $visitor; + } + + /** + * Create a request for the command and operation + * + * @param CommandInterface $command Command to create a request for + * + * @return RequestInterface + */ + protected function createRequest(CommandInterface $command) + { + $operation = $command->getOperation(); + $client = $command->getClient(); + $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); + + // If the command does not specify a template, then assume the base URL of the client + if (!($uri = $operation->getUri())) { + return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); + } + + // Get the path values and use the client config settings + $variables = array(); + foreach ($operation->getParams() as $name => $arg) { + if ($arg->getLocation() == 'uri') { + if (isset($command[$name])) { + $variables[$name] = $arg->filter($command[$name]); + if (!is_array($variables[$name])) { + $variables[$name] = (string) $variables[$name]; + } + } + } + } + + return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php new file mode 100644 index 000000000..4fe380376 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php @@ -0,0 +1,55 @@ +getRequest()->getResponse(); + + // Account for hard coded content-type values specified in service descriptions + if ($contentType = $command['command.expects']) { + $response->setHeader('Content-Type', $contentType); + } else { + $contentType = (string) $response->getHeader('Content-Type'); + } + + return $this->handleParsing($command, $response, $contentType); + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $result = $response; + if ($result->getBody()) { + if (stripos($contentType, 'json') !== false) { + $result = $result->json(); + } elseif (stripos($contentType, 'xml') !== false) { + $result = $result->xml(); + } + } + + return $result; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100644 index 000000000..1c5ce0741 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,39 @@ +client = $client; + $this->aliases = $aliases; + } + + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100644 index 000000000..8c46983d6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,154 @@ +getDescription()) { + $factories[] = new ServiceDescriptionFactory($description); + } + $factories[] = new ConcreteClassFactory($client); + + return new self($factories); + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object + * matching a class name. + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + public function count() + { + return count($this->factories); + } + + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100644 index 000000000..0e93deaa0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,47 @@ +client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the current client and the default directory + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100644 index 000000000..35c299d9d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +map = $map; + } + + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100644 index 000000000..b943a5b50 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,71 @@ +setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescriptionInterface $description Service description to use + * + * @return FactoryInterface + */ + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescriptionInterface + */ + public function getServiceDescription() + { + return $this->description; + } + + public function factory($name, array $args = array()) + { + $command = $this->description->getOperation($name); + + // If a command wasn't found, then try to uppercase the first letter and try again + if (!$command) { + $command = $this->description->getOperation(ucfirst($name)); + // If an inflector was passed, then attempt to get the command using snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getOperation($this->inflector->snake($name)); + } + } + + if ($command) { + $class = $command->getClass(); + return new $class($args, $command, $this->description); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php new file mode 100644 index 000000000..adcfca1ba --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php @@ -0,0 +1,69 @@ +resolveRecursively($value, $param) + : $param->filter($value); + } + + /** + * Map nested parameters into the location_key based parameters + * + * @param array $value Value to map + * @param Parameter $param Parameter that holds information about the current key + * + * @return array Returns the mapped array + */ + protected function resolveRecursively(array $value, Parameter $param) + { + foreach ($value as $name => &$v) { + switch ($param->getType()) { + case 'object': + if ($subParam = $param->getProperty($name)) { + $key = $subParam->getWireName(); + $value[$key] = $this->prepareValue($v, $subParam); + if ($name != $key) { + unset($value[$name]); + } + } elseif ($param->getAdditionalProperties() instanceof Parameter) { + $v = $this->prepareValue($v, $param->getAdditionalProperties()); + } + break; + case 'array': + if ($items = $param->getItems()) { + $v = $this->prepareValue($v, $items); + } + break; + } + } + + return $param->filter($value); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php new file mode 100644 index 000000000..168d7806f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php @@ -0,0 +1,58 @@ +filter($value); + $entityBody = EntityBody::factory($value); + $request->setBody($entityBody); + $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); + // Add the Content-Encoding header if one is set on the EntityBody + if ($encoding = $entityBody->getContentEncoding()) { + $request->setHeader('Content-Encoding', $encoding); + } + } + + /** + * Add the appropriate expect header to a request + * + * @param EntityEnclosingRequestInterface $request Request to update + * @param EntityBodyInterface $body Entity body of the request + * @param string|int $expect Expect header setting + */ + protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) + { + // Allow the `expect` data parameter to be set to remove the Expect header from the request + if ($expect === false) { + $request->removeHeader('Expect'); + } elseif ($expect !== true) { + // Default to using a MB as the point in which to start using the expect header + $expect = $expect ?: 1048576; + // If the expect_header value is numeric then only add if the size is greater than the cutoff + if (is_numeric($expect) && $body->getSize()) { + if ($body->getSize() < $expect) { + $request->removeHeader('Expect'); + } else { + $request->setHeader('Expect', '100-Continue'); + } + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php new file mode 100644 index 000000000..2a537542c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php @@ -0,0 +1,44 @@ +filter($value); + if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->addPrefixedHeaders($request, $param, $value); + } else { + $request->setHeader($param->getWireName(), $value); + } + } + + /** + * Add a prefixed array of headers to the request + * + * @param RequestInterface $request Request to update + * @param Parameter $param Parameter object + * @param array $value Header array to add + * + * @throws InvalidArgumentException + */ + protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) + { + if (!is_array($value)) { + throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); + } + $prefix = $param->getSentAs(); + foreach ($value as $headerName => $headerValue) { + $request->setHeader($prefix . $headerName, $headerValue); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php new file mode 100644 index 000000000..757e1c520 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php @@ -0,0 +1,63 @@ +data = new \SplObjectStorage(); + } + + /** + * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a + * Content-Type header unless you specify one here. + * + * @param string $header Header to set when JSON is added (e.g. application/json) + * + * @return self + */ + public function setContentTypeHeader($header = 'application/json') + { + $this->jsonContentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + if (isset($this->data[$command])) { + $json = $this->data[$command]; + } else { + $json = array(); + } + $json[$param->getWireName()] = $this->prepareValue($value, $param); + $this->data[$command] = $json; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + if (isset($this->data[$command])) { + // Don't overwrite the Content-Type if one is set + if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->jsonContentType); + } + + $request->setBody(json_encode($this->data[$command])); + unset($this->data[$command]); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php new file mode 100644 index 000000000..975850b74 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php @@ -0,0 +1,18 @@ +setPostField($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php new file mode 100644 index 000000000..0853ebe62 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php @@ -0,0 +1,24 @@ +filter($value); + if ($value instanceof PostFileInterface) { + $request->addPostFile($value); + } else { + $request->addPostFile($param->getWireName(), $value); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php new file mode 100644 index 000000000..315877aa0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php @@ -0,0 +1,18 @@ +getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php new file mode 100644 index 000000000..14e0b2d2b --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php @@ -0,0 +1,31 @@ +setResponseBody($value); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php new file mode 100644 index 000000000..5b7148787 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php @@ -0,0 +1,252 @@ +data = new \SplObjectStorage(); + } + + /** + * Change the content-type header that is added when XML is found + * + * @param string $header Header to set when XML is found + * + * @return self + */ + public function setContentTypeHeader($header) + { + $this->contentType = $header; + + return $this; + } + + public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) + { + $xml = isset($this->data[$command]) + ? $this->data[$command] + : $this->createRootElement($param->getParent()); + $this->addXml($xml, $param, $value); + + $this->data[$command] = $xml; + } + + public function after(CommandInterface $command, RequestInterface $request) + { + $xml = null; + + // If data was found that needs to be serialized, then do so + if (isset($this->data[$command])) { + $xml = $this->finishDocument($this->data[$command]); + unset($this->data[$command]); + } else { + // Check if XML should always be sent for the command + $operation = $command->getOperation(); + if ($operation->getData('xmlAllowEmpty')) { + $xmlWriter = $this->createRootElement($operation); + $xml = $this->finishDocument($xmlWriter); + } + } + + if ($xml) { + // Don't overwrite the Content-Type if one is set + if ($this->contentType && !$request->hasHeader('Content-Type')) { + $request->setHeader('Content-Type', $this->contentType); + } + $request->setBody($xml); + } + } + + /** + * Create the root XML element to use with a request + * + * @param Operation $operation Operation object + * + * @return \XMLWriter + */ + protected function createRootElement(Operation $operation) + { + static $defaultRoot = array('name' => 'Request'); + // If no root element was specified, then just wrap the XML in 'Request' + $root = $operation->getData('xmlRoot') ?: $defaultRoot; + // Allow the XML declaration to be customized with xmlEncoding + $encoding = $operation->getData('xmlEncoding'); + + $xmlWriter = $this->startDocument($encoding); + + $xmlWriter->startElement($root['name']); + // Create the wrapping element with no namespaces if no namespaces were present + if (!empty($root['namespaces'])) { + // Create the wrapping element with an array of one or more namespaces + foreach ((array) $root['namespaces'] as $prefix => $uri) { + $nsLabel = 'xmlns'; + if (!is_numeric($prefix)) { + $nsLabel .= ':'.$prefix; + } + $xmlWriter->writeAttribute($nsLabel, $uri); + } + } + return $xmlWriter; + } + + /** + * Recursively build the XML body + * + * @param \XMLWriter $xmlWriter XML to modify + * @param Parameter $param API Parameter + * @param mixed $value Value to add + */ + protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) + { + if ($value === null) { + return; + } + + $value = $param->filter($value); + $type = $param->getType(); + $name = $param->getWireName(); + $prefix = null; + $namespace = $param->getData('xmlNamespace'); + if (false !== strpos($name, ':')) { + list($prefix, $name) = explode(':', $name, 2); + } + + if ($type == 'object' || $type == 'array') { + if (!$param->getData('xmlFlattened')) { + $xmlWriter->startElementNS(null, $name, $namespace); + } + if ($param->getType() == 'array') { + $this->addXmlArray($xmlWriter, $param, $value); + } elseif ($param->getType() == 'object') { + $this->addXmlObject($xmlWriter, $param, $value); + } + if (!$param->getData('xmlFlattened')) { + $xmlWriter->endElement(); + } + return; + } + if ($param->getData('xmlAttribute')) { + $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); + } else { + $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); + } + } + + /** + * Write an attribute with namespace if used + * + * @param \XMLWriter $xmlWriter XMLWriter instance + * @param string $prefix Namespace prefix if any + * @param string $name Attribute name + * @param string $namespace The uri of the namespace + * @param string $value The attribute content + */ + protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) + { + if (empty($namespace)) { + $xmlWriter->writeAttribute($name, $value); + } else { + $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); + } + } + + /** + * Write an element with namespace if used + * + * @param \XMLWriter $xmlWriter XML writer resource + * @param string $prefix Namespace prefix if any + * @param string $name Element name + * @param string $namespace The uri of the namespace + * @param string $value The element content + */ + protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) + { + $xmlWriter->startElementNS($prefix, $name, $namespace); + if (strpbrk($value, '<>&')) { + $xmlWriter->writeCData($value); + } else { + $xmlWriter->writeRaw($value); + } + $xmlWriter->endElement(); + } + + /** + * Create a new xml writer and start a document + * + * @param string $encoding document encoding + * + * @return \XMLWriter the writer resource + */ + protected function startDocument($encoding) + { + $xmlWriter = new \XMLWriter(); + $xmlWriter->openMemory(); + $xmlWriter->startDocument('1.0', $encoding); + + return $xmlWriter; + } + + /** + * End the document and return the output + * + * @param \XMLWriter $xmlWriter + * + * @return \string the writer resource + */ + protected function finishDocument($xmlWriter) + { + $xmlWriter->endDocument(); + + return $xmlWriter->outputMemory(); + } + + /** + * Add an array to the XML + */ + protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + if ($items = $param->getItems()) { + foreach ($value as $v) { + $this->addXml($xmlWriter, $items, $v); + } + } + } + + /** + * Add an object to the XML + */ + protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) + { + $noAttributes = array(); + // add values which have attributes + foreach ($value as $name => $v) { + if ($property = $param->getProperty($name)) { + if ($property->getData('xmlAttribute')) { + $this->addXml($xmlWriter, $property, $v); + } else { + $noAttributes[] = array('value' => $v, 'property' => $property); + } + } + } + // now add values with no attributes + foreach ($noAttributes as $element) { + $this->addXml($xmlWriter, $element['property'], $element['value']); + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php new file mode 100644 index 000000000..d87eeb945 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php @@ -0,0 +1,26 @@ +getName()] = $param->filter($response->getBody()); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php new file mode 100644 index 000000000..0f8737cbd --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php @@ -0,0 +1,50 @@ +getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { + $this->processPrefixedHeaders($response, $param, $value); + } else { + $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); + } + } + + /** + * Process a prefixed header array + * + * @param Response $response Response that contains the headers + * @param Parameter $param Parameter object + * @param array $value Value response array to modify + */ + protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) + { + // Grab prefixed headers that should be placed into an array with the prefix stripped + if ($prefix = $param->getSentAs()) { + $container = $param->getName(); + $len = strlen($prefix); + // Find all matching headers and place them into the containing element + foreach ($response->getHeaders()->toArray() as $key => $header) { + if (stripos($key, $prefix) === 0) { + // Account for multi-value headers + $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; + } + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php new file mode 100644 index 000000000..a609ebd8c --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php @@ -0,0 +1,93 @@ +getResponse()->json(); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $name = $param->getName(); + $key = $param->getWireName(); + if (isset($value[$key])) { + $this->recursiveProcess($param, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + if ($value === null) { + return; + } + + if (is_array($value)) { + $type = $param->getType(); + if ($type == 'array') { + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } elseif ($type == 'object' && !isset($value[0])) { + // On the above line, we ensure that the array is associative and not numerically indexed + $knownProperties = array(); + if ($properties = $param->getProperties()) { + foreach ($properties as $property) { + $name = $property->getName(); + $key = $property->getWireName(); + $knownProperties[$name] = 1; + if (isset($value[$key])) { + $this->recursiveProcess($property, $value[$key]); + if ($key != $name) { + $value[$name] = $value[$key]; + unset($value[$key]); + } + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } elseif (($additional = $param->getAdditionalProperties()) !== true) { + // Validate and filter additional properties + foreach ($value as &$v) { + $this->recursiveProcess($additional, $v); + } + } + } + } + + $value = $param->filter($value); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php new file mode 100644 index 000000000..1b10ebce7 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php @@ -0,0 +1,23 @@ +getName()] = $response->getReasonPhrase(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php new file mode 100644 index 000000000..033f40c3f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php @@ -0,0 +1,46 @@ +getName()] = $response->getStatusCode(); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php new file mode 100644 index 000000000..bb7124be7 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php @@ -0,0 +1,151 @@ +getResponse()->xml()), true); + } + + public function visit( + CommandInterface $command, + Response $response, + Parameter $param, + &$value, + $context = null + ) { + $sentAs = $param->getWireName(); + $name = $param->getName(); + if (isset($value[$sentAs])) { + $this->recursiveProcess($param, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + /** + * Recursively process a parameter while applying filters + * + * @param Parameter $param API parameter being processed + * @param mixed $value Value to validate and process. The value may change during this process. + */ + protected function recursiveProcess(Parameter $param, &$value) + { + $type = $param->getType(); + + if (!is_array($value)) { + if ($type == 'array') { + // Cast to an array if the value was a string, but should be an array + $this->recursiveProcess($param->getItems(), $value); + $value = array($value); + } + } elseif ($type == 'object') { + $this->processObject($param, $value); + } elseif ($type == 'array') { + $this->processArray($param, $value); + } elseif ($type == 'string' && gettype($value) == 'array') { + $value = ''; + } + + if ($value !== null) { + $value = $param->filter($value); + } + } + + /** + * Process an array + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processArray(Parameter $param, &$value) + { + // Convert the node if it was meant to be an array + if (!isset($value[0])) { + // Collections fo nodes are sometimes wrapped in an additional array. For example: + // 12 should become: + // array('Items' => array(array('a' => 1), array('a' => 2)) + // Some nodes are not wrapped. For example: 12 + // should become array('Foo' => array(array('a' => 1), array('a' => 2)) + if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { + // Account for the case of a collection wrapping wrapped nodes: Items => Item[] + $value = $value[$param->getItems()->getWireName()]; + // If the wrapped node only had one value, then make it an array of nodes + if (!isset($value[0]) || !is_array($value)) { + $value = array($value); + } + } elseif (!empty($value)) { + // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the + // value is set and not empty + $value = array($value); + } + } + + foreach ($value as &$item) { + $this->recursiveProcess($param->getItems(), $item); + } + } + + /** + * Process an object + * + * @param Parameter $param API parameter being parsed + * @param mixed $value Value to process + */ + protected function processObject(Parameter $param, &$value) + { + // Ensure that the array is associative and not numerically indexed + if (!isset($value[0]) && ($properties = $param->getProperties())) { + $knownProperties = array(); + foreach ($properties as $property) { + $name = $property->getName(); + $sentAs = $property->getWireName(); + $knownProperties[$name] = 1; + if ($property->getData('xmlAttribute')) { + $this->processXmlAttribute($property, $value); + } elseif (isset($value[$sentAs])) { + $this->recursiveProcess($property, $value[$sentAs]); + if ($name != $sentAs) { + $value[$name] = $value[$sentAs]; + unset($value[$sentAs]); + } + } + } + + // Remove any unknown and potentially unsafe properties + if ($param->getAdditionalProperties() === false) { + $value = array_intersect_key($value, $knownProperties); + } + } + } + + /** + * Process an XML attribute property + * + * @param Parameter $property Property to process + * @param array $value Value to process and update + */ + protected function processXmlAttribute(Parameter $property, array &$value) + { + $sentAs = $property->getWireName(); + if (isset($value['@attributes'][$sentAs])) { + $value[$property->getName()] = $value['@attributes'][$sentAs]; + unset($value['@attributes'][$sentAs]); + if (empty($value['@attributes'])) { + unset($value['@attributes']); + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php new file mode 100644 index 000000000..74cb62813 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php @@ -0,0 +1,138 @@ + 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', + 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', + 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', + 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', + 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', + 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', + 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', + 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', + 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', + 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', + 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', + 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', + 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', + 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' + ); + + /** @var array Array of mappings of location names to classes */ + protected $mappings; + + /** @var array Cache of instantiated visitors */ + protected $cache = array(); + + /** + * @return self + * @codeCoverageIgnore + */ + public static function getInstance() + { + if (!self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to + * use the default values. + */ + public function __construct(array $mappings = null) + { + $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; + } + + /** + * Get an instance of a request visitor by location name + * + * @param string $visitor Visitor name + * + * @return RequestVisitorInterface + */ + public function getRequestVisitor($visitor) + { + return $this->getKey('request.' . $visitor); + } + + /** + * Get an instance of a response visitor by location name + * + * @param string $visitor Visitor name + * + * @return ResponseVisitorInterface + */ + public function getResponseVisitor($visitor) + { + return $this->getKey('response.' . $visitor); + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param RequestVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addRequestVisitor($name, RequestVisitorInterface $visitor) + { + $this->cache['request.' . $name] = $visitor; + + return $this; + } + + /** + * Add a response visitor to the factory by name + * + * @param string $name Name of the visitor + * @param ResponseVisitorInterface $visitor Visitor to add + * + * @return self + */ + public function addResponseVisitor($name, ResponseVisitorInterface $visitor) + { + $this->cache['response.' . $name] = $visitor; + + return $this; + } + + /** + * Get a visitor by key value name + * + * @param string $key Key name to retrieve + * + * @return mixed + * @throws InvalidArgumentException + */ + private function getKey($key) + { + if (!isset($this->cache[$key])) { + if (!isset($this->mappings[$key])) { + list($type, $name) = explode('.', $key); + throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); + } + $this->cache[$key] = new $this->mappings[$key]; + } + + return $this->cache[$key]; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php new file mode 100644 index 000000000..0748b5af0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php @@ -0,0 +1,89 @@ +responseParser = $parser; + + return $this; + } + + /** + * Set the request serializer used with the command + * + * @param RequestSerializerInterface $serializer Request serializer + * + * @return self + */ + public function setRequestSerializer(RequestSerializerInterface $serializer) + { + $this->requestSerializer = $serializer; + + return $this; + } + + /** + * Get the request serializer used with the command + * + * @return RequestSerializerInterface + */ + public function getRequestSerializer() + { + if (!$this->requestSerializer) { + // Use the default request serializer if none was found + $this->requestSerializer = DefaultRequestSerializer::getInstance(); + } + + return $this->requestSerializer; + } + + /** + * Get the response parser used for the operation + * + * @return ResponseParserInterface + */ + public function getResponseParser() + { + if (!$this->responseParser) { + // Use the default response parser if none was found + $this->responseParser = OperationResponseParser::getInstance(); + } + + return $this->responseParser; + } + + protected function build() + { + // Prepare and serialize the request + $this->request = $this->getRequestSerializer()->prepare($this); + } + + protected function process() + { + // Do not process the response if 'command.response_processing' is set to 'raw' + $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW + ? $this->request->getResponse() + : $this->getResponseParser()->parse($this); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php new file mode 100644 index 000000000..ca00bc062 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php @@ -0,0 +1,195 @@ +factory = $factory; + $this->schemaInModels = $schemaInModels; + } + + /** + * Add a location visitor to the command + * + * @param string $location Location to associate with the visitor + * @param ResponseVisitorInterface $visitor Visitor to attach + * + * @return self + */ + public function addVisitor($location, ResponseVisitorInterface $visitor) + { + $this->factory->addResponseVisitor($location, $visitor); + + return $this; + } + + protected function handleParsing(CommandInterface $command, Response $response, $contentType) + { + $operation = $command->getOperation(); + $type = $operation->getResponseType(); + $model = null; + + if ($type == OperationInterface::TYPE_MODEL) { + $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); + } elseif ($type == OperationInterface::TYPE_CLASS) { + return $this->parseClass($command); + } + + if (!$model) { + // Return basic processing if the responseType is not model or the model cannot be found + return parent::handleParsing($command, $response, $contentType); + } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { + // Returns a model with no visiting if the command response processing is not model + return new Model(parent::handleParsing($command, $response, $contentType)); + } else { + // Only inject the schema into the model if "schemaInModel" is true + return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); + } + } + + /** + * Parse a class object + * + * @param CommandInterface $command Command to parse into an object + * + * @return mixed + * @throws ResponseClassException + */ + protected function parseClass(CommandInterface $command) + { + // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result + $event = new CreateResponseClassEvent(array('command' => $command)); + $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); + if ($result = $event->getResult()) { + return $result; + } + + $className = $command->getOperation()->getResponseClass(); + if (!method_exists($className, 'fromCommand')) { + throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); + } + + return $className::fromCommand($command); + } + + /** + * Perform transformations on the result array + * + * @param Parameter $model Model that defines the structure + * @param CommandInterface $command Command that performed the operation + * @param Response $response Response received + * + * @return array Returns the array of result data + */ + protected function visitResult(Parameter $model, CommandInterface $command, Response $response) + { + $foundVisitors = $result = $knownProps = array(); + $props = $model->getProperties(); + + foreach ($props as $schema) { + if ($location = $schema->getLocation()) { + // Trigger the before method on the first found visitor of this type + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + } + } + + // Visit additional properties when it is an actual schema + if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { + $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); + } + + // Apply the parameter value with the location visitor + foreach ($props as $schema) { + $knownProps[$schema->getName()] = 1; + if ($location = $schema->getLocation()) { + $foundVisitors[$location]->visit($command, $response, $schema, $result); + } + } + + // Remove any unknown and potentially unsafe top-level properties + if ($additional === false) { + $result = array_intersect_key($result, $knownProps); + } + + // Call the after() method of each found visitor + foreach ($foundVisitors as $visitor) { + $visitor->after($command); + } + + return $result; + } + + protected function visitAdditionalProperties( + Parameter $model, + CommandInterface $command, + Response $response, + Parameter $additional, + &$result, + array &$foundVisitors + ) { + // Only visit when a location is specified + if ($location = $additional->getLocation()) { + if (!isset($foundVisitors[$location])) { + $foundVisitors[$location] = $this->factory->getResponseVisitor($location); + $foundVisitors[$location]->before($command, $result); + } + // Only traverse if an array was parsed from the before() visitors + if (is_array($result)) { + // Find each additional property + foreach (array_keys($result) as $key) { + // Check if the model actually knows this property. If so, then it is not additional + if (!$model->getProperty($key)) { + // Set the name to the key so that we can parse it with each visitor + $additional->setName($key); + $foundVisitors[$location]->visit($command, $response, $additional, $result); + } + } + // Reset the additionalProperties name to null + $additional->setName(null); + } + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php new file mode 100644 index 000000000..60b9334d4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php @@ -0,0 +1,21 @@ + true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true, + 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true, + 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true, + 'errorResponses' => true + ); + + /** @var array Parameters */ + protected $parameters = array(); + + /** @var Parameter Additional parameters schema */ + protected $additionalParameters; + + /** @var string Name of the command */ + protected $name; + + /** @var string HTTP method */ + protected $httpMethod; + + /** @var string This is a short summary of what the operation does */ + protected $summary; + + /** @var string A longer text field to explain the behavior of the operation. */ + protected $notes; + + /** @var string Reference URL providing more information about the operation */ + protected $documentationUrl; + + /** @var string HTTP URI of the command */ + protected $uri; + + /** @var string Class of the command object */ + protected $class; + + /** @var string This is what is returned from the method */ + protected $responseClass; + + /** @var string Type information about the response */ + protected $responseType; + + /** @var string Information about the response returned by the operation */ + protected $responseNotes; + + /** @var bool Whether or not the command is deprecated */ + protected $deprecated; + + /** @var array Array of errors that could occur when running the command */ + protected $errorResponses; + + /** @var ServiceDescriptionInterface */ + protected $description; + + /** @var array Extra operation information */ + protected $data; + + /** + * Builds an Operation object using an array of configuration data: + * - name: (string) Name of the command + * - httpMethod: (string) HTTP method of the operation + * - uri: (string) URI template that can create a relative or absolute URL + * - class: (string) Concrete class that implements this command + * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information. + * - summary: (string) This is a short summary of what the operation does + * - notes: (string) A longer text field to explain the behavior of the operation. + * - documentationUrl: (string) Reference URL providing more information about the operation + * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant + * class name, or model. + * - responseNotes: (string) Information about the response returned by the operation + * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this + * value will be automatically inferred based on whether or not there is a model matching the + * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default. + * - deprecated: (bool) Set to true if this is a deprecated command + * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a + * 'code' (the HTTP response code), 'phrase' (response reason phrase or description of the + * error), and 'class' (a custom exception class that would be thrown if the error is + * encountered). + * - data: (array) Any extra data that might be used to help build or serialize the operation + * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is + * not in the schema + * + * @param array $config Array of configuration data + * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found + */ + public function __construct(array $config = array(), ServiceDescriptionInterface $description = null) + { + $this->description = $description; + + // Get the intersection of the available properties and properties set on the operation + foreach (array_intersect_key($config, self::$properties) as $key => $value) { + $this->{$key} = $value; + } + + $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS; + $this->deprecated = (bool) $this->deprecated; + $this->errorResponses = $this->errorResponses ?: array(); + $this->data = $this->data ?: array(); + + if (!$this->responseClass) { + $this->responseClass = 'array'; + $this->responseType = 'primitive'; + } elseif ($this->responseType) { + // Set the response type to perform validation + $this->setResponseType($this->responseType); + } else { + // A response class was set and no response type was set, so guess what the type is + $this->inferResponseType(); + } + + // Parameters need special handling when adding + if ($this->parameters) { + foreach ($this->parameters as $name => $param) { + if ($param instanceof Parameter) { + $param->setName($name)->setParent($this); + } elseif (is_array($param)) { + $param['name'] = $name; + $this->addParam(new Parameter($param, $this->description)); + } + } + } + + if ($this->additionalParameters) { + if ($this->additionalParameters instanceof Parameter) { + $this->additionalParameters->setParent($this); + } elseif (is_array($this->additionalParameters)) { + $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description)); + } + } + } + + public function toArray() + { + $result = array(); + // Grab valid properties and filter out values that weren't set + foreach (array_keys(self::$properties) as $check) { + if ($value = $this->{$check}) { + $result[$check] = $value; + } + } + // Remove the name property + unset($result['name']); + // Parameters need to be converted to arrays + $result['parameters'] = array(); + foreach ($this->parameters as $key => $param) { + $result['parameters'][$key] = $param->toArray(); + } + // Additional parameters need to be cast to an array + if ($this->additionalParameters instanceof Parameter) { + $result['additionalParameters'] = $this->additionalParameters->toArray(); + } + + return $result; + } + + public function getServiceDescription() + { + return $this->description; + } + + public function setServiceDescription(ServiceDescriptionInterface $description) + { + $this->description = $description; + + return $this; + } + + public function getParams() + { + return $this->parameters; + } + + public function getParamNames() + { + return array_keys($this->parameters); + } + + public function hasParam($name) + { + return isset($this->parameters[$name]); + } + + public function getParam($param) + { + return isset($this->parameters[$param]) ? $this->parameters[$param] : null; + } + + /** + * Add a parameter to the command + * + * @param Parameter $param Parameter to add + * + * @return self + */ + public function addParam(Parameter $param) + { + $this->parameters[$param->getName()] = $param; + $param->setParent($this); + + return $this; + } + + /** + * Remove a parameter from the command + * + * @param string $name Name of the parameter to remove + * + * @return self + */ + public function removeParam($name) + { + unset($this->parameters[$name]); + + return $this; + } + + public function getHttpMethod() + { + return $this->httpMethod; + } + + /** + * Set the HTTP method of the command + * + * @param string $httpMethod Method to set + * + * @return self + */ + public function setHttpMethod($httpMethod) + { + $this->httpMethod = $httpMethod; + + return $this; + } + + public function getClass() + { + return $this->class; + } + + /** + * Set the concrete class of the command + * + * @param string $className Concrete class name + * + * @return self + */ + public function setClass($className) + { + $this->class = $className; + + return $this; + } + + public function getName() + { + return $this->name; + } + + /** + * Set the name of the command + * + * @param string $name Name of the command + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + public function getSummary() + { + return $this->summary; + } + + /** + * Set a short summary of what the operation does + * + * @param string $summary Short summary of the operation + * + * @return self + */ + public function setSummary($summary) + { + $this->summary = $summary; + + return $this; + } + + public function getNotes() + { + return $this->notes; + } + + /** + * Set a longer text field to explain the behavior of the operation. + * + * @param string $notes Notes on the operation + * + * @return self + */ + public function setNotes($notes) + { + $this->notes = $notes; + + return $this; + } + + public function getDocumentationUrl() + { + return $this->documentationUrl; + } + + /** + * Set the URL pointing to additional documentation on the command + * + * @param string $docUrl Documentation URL + * + * @return self + */ + public function setDocumentationUrl($docUrl) + { + $this->documentationUrl = $docUrl; + + return $this; + } + + public function getResponseClass() + { + return $this->responseClass; + } + + /** + * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array', + * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID). + * + * @param string $responseClass Type of response + * + * @return self + */ + public function setResponseClass($responseClass) + { + $this->responseClass = $responseClass; + $this->inferResponseType(); + + return $this; + } + + public function getResponseType() + { + return $this->responseType; + } + + /** + * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation' + * + * @param string $responseType Response type information + * + * @return self + * @throws InvalidArgumentException + */ + public function setResponseType($responseType) + { + static $types = array( + self::TYPE_PRIMITIVE => true, + self::TYPE_CLASS => true, + self::TYPE_MODEL => true, + self::TYPE_DOCUMENTATION => true + ); + if (!isset($types[$responseType])) { + throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types))); + } + + $this->responseType = $responseType; + + return $this; + } + + public function getResponseNotes() + { + return $this->responseNotes; + } + + /** + * Set notes about the response of the operation + * + * @param string $notes Response notes + * + * @return self + */ + public function setResponseNotes($notes) + { + $this->responseNotes = $notes; + + return $this; + } + + public function getDeprecated() + { + return $this->deprecated; + } + + /** + * Set whether or not the command is deprecated + * + * @param bool $isDeprecated Set to true to mark as deprecated + * + * @return self + */ + public function setDeprecated($isDeprecated) + { + $this->deprecated = $isDeprecated; + + return $this; + } + + public function getUri() + { + return $this->uri; + } + + /** + * Set the URI template of the command + * + * @param string $uri URI template to set + * + * @return self + */ + public function setUri($uri) + { + $this->uri = $uri; + + return $this; + } + + public function getErrorResponses() + { + return $this->errorResponses; + } + + /** + * Add an error to the command + * + * @param string $code HTTP response code + * @param string $reason HTTP response reason phrase or information about the error + * @param string $class Exception class associated with the error + * + * @return self + */ + public function addErrorResponse($code, $reason, $class) + { + $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class); + + return $this; + } + + /** + * Set all of the error responses of the operation + * + * @param array $errorResponses Hash of error name to a hash containing a code, reason, class + * + * @return self + */ + public function setErrorResponses(array $errorResponses) + { + $this->errorResponses = $errorResponses; + + return $this; + } + + public function getData($name) + { + return isset($this->data[$name]) ? $this->data[$name] : null; + } + + /** + * Set a particular data point on the operation + * + * @param string $name Name of the data value + * @param mixed $value Value to set + * + * @return self + */ + public function setData($name, $value) + { + $this->data[$name] = $value; + + return $this; + } + + /** + * Get the additionalParameters of the operation + * + * @return Parameter|null + */ + public function getAdditionalParameters() + { + return $this->additionalParameters; + } + + /** + * Set the additionalParameters of the operation + * + * @param Parameter|null $parameter Parameter to set + * + * @return self + */ + public function setAdditionalParameters($parameter) + { + if ($this->additionalParameters = $parameter) { + $this->additionalParameters->setParent($this); + } + + return $this; + } + + /** + * Infer the response type from the responseClass value + */ + protected function inferResponseType() + { + static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1); + if (isset($primitives[$this->responseClass])) { + $this->responseType = self::TYPE_PRIMITIVE; + } elseif ($this->description && $this->description->hasModel($this->responseClass)) { + $this->responseType = self::TYPE_MODEL; + } else { + $this->responseType = self::TYPE_CLASS; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php new file mode 100644 index 000000000..4de41bd67 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php @@ -0,0 +1,159 @@ +getModel($data['$ref'])) { + $data = $model->toArray() + $data; + } + } elseif (isset($data['extends'])) { + // If this parameter extends from another parameter then start with the actual data + // union in the parent's data (e.g. actual supersedes parent) + if ($extends = $description->getModel($data['extends'])) { + $data += $extends->toArray(); + } + } + } + + // Pull configuration data into the parameter + foreach ($data as $key => $value) { + $this->{$key} = $value; + } + + $this->serviceDescription = $description; + $this->required = (bool) $this->required; + $this->data = (array) $this->data; + + if ($this->filters) { + $this->setFilters((array) $this->filters); + } + + if ($this->type == 'object' && $this->additionalProperties === null) { + $this->additionalProperties = true; + } + } + + /** + * Convert the object to an array + * + * @return array + */ + public function toArray() + { + static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs', + 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum', + 'filters'); + + $result = array(); + + // Anything that is in the `Items` attribute of an array *must* include it's name if available + if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) { + $result['name'] = $this->name; + } + + foreach ($checks as $c) { + if ($value = $this->{$c}) { + $result[$c] = $value; + } + } + + if ($this->default !== null) { + $result['default'] = $this->default; + } + + if ($this->items !== null) { + $result['items'] = $this->getItems()->toArray(); + } + + if ($this->additionalProperties !== null) { + $result['additionalProperties'] = $this->getAdditionalProperties(); + if ($result['additionalProperties'] instanceof self) { + $result['additionalProperties'] = $result['additionalProperties']->toArray(); + } + } + + if ($this->type == 'object' && $this->properties) { + $result['properties'] = array(); + foreach ($this->getProperties() as $name => $property) { + $result['properties'][$name] = $property->toArray(); + } + } + + return $result; + } + + /** + * Get the default or static value of the command based on a value + * + * @param string $value Value that is currently set + * + * @return mixed Returns the value, a static value if one is present, or a default value + */ + public function getValue($value) + { + if ($this->static || ($this->default !== null && $value === null)) { + return $this->default; + } + + return $value; + } + + /** + * Run a value through the filters OR format attribute associated with the parameter + * + * @param mixed $value Value to filter + * + * @return mixed Returns the filtered value + */ + public function filter($value) + { + // Formats are applied exclusively and supersed filters + if ($this->format) { + return SchemaFormatter::format($this->format, $value); + } + + // Convert Boolean values + if ($this->type == 'boolean' && !is_bool($value)) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); + } + + // Apply filters to the value + if ($this->filters) { + foreach ($this->filters as $filter) { + if (is_array($filter)) { + // Convert complex filters that hold value place holders + foreach ($filter['args'] as &$data) { + if ($data == '@value') { + $data = $value; + } elseif ($data == '@api') { + $data = $this; + } + } + $value = call_user_func_array($filter['method'], $filter['args']); + } else { + $value = call_user_func($filter, $value); + } + } + } + + return $value; + } + + /** + * Get the name of the parameter + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the key of the parameter, where sentAs will supersede name if it is set + * + * @return string + */ + public function getWireName() + { + return $this->sentAs ?: $this->name; + } + + /** + * Set the name of the parameter + * + * @param string $name Name to set + * + * @return self + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Get the type(s) of the parameter + * + * @return string|array + */ + public function getType() + { + return $this->type; + } + + /** + * Set the type(s) of the parameter + * + * @param string|array $type Type of parameter or array of simple types used in a union + * + * @return self + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * Get if the parameter is required + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + + /** + * Set if the parameter is required + * + * @param bool $isRequired Whether or not the parameter is required + * + * @return self + */ + public function setRequired($isRequired) + { + $this->required = (bool) $isRequired; + + return $this; + } + + /** + * Get the default value of the parameter + * + * @return string|null + */ + public function getDefault() + { + return $this->default; + } + + /** + * Set the default value of the parameter + * + * @param string|null $default Default value to set + * + * @return self + */ + public function setDefault($default) + { + $this->default = $default; + + return $this; + } + + /** + * Get the description of the parameter + * + * @return string|null + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set the description of the parameter + * + * @param string $description Description + * + * @return self + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Get the minimum acceptable value for an integer + * + * @return int|null + */ + public function getMinimum() + { + return $this->minimum; + } + + /** + * Set the minimum acceptable value for an integer + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinimum($min) + { + $this->minimum = $min; + + return $this; + } + + /** + * Get the maximum acceptable value for an integer + * + * @return int|null + */ + public function getMaximum() + { + return $this->maximum; + } + + /** + * Set the maximum acceptable value for an integer + * + * @param int $max Maximum + * + * @return self + */ + public function setMaximum($max) + { + $this->maximum = $max; + + return $this; + } + + /** + * Get the minimum allowed length of a string value + * + * @return int + */ + public function getMinLength() + { + return $this->minLength; + } + + /** + * Set the minimum allowed length of a string value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinLength($min) + { + $this->minLength = $min; + + return $this; + } + + /** + * Get the maximum allowed length of a string value + * + * @return int|null + */ + public function getMaxLength() + { + return $this->maxLength; + } + + /** + * Set the maximum allowed length of a string value + * + * @param int $max Maximum length + * + * @return self + */ + public function setMaxLength($max) + { + $this->maxLength = $max; + + return $this; + } + + /** + * Get the maximum allowed number of items in an array value + * + * @return int|null + */ + public function getMaxItems() + { + return $this->maxItems; + } + + /** + * Set the maximum allowed number of items in an array value + * + * @param int $max Maximum + * + * @return self + */ + public function setMaxItems($max) + { + $this->maxItems = $max; + + return $this; + } + + /** + * Get the minimum allowed number of items in an array value + * + * @return int + */ + public function getMinItems() + { + return $this->minItems; + } + + /** + * Set the minimum allowed number of items in an array value + * + * @param int|null $min Minimum + * + * @return self + */ + public function setMinItems($min) + { + $this->minItems = $min; + + return $this; + } + + /** + * Get the location of the parameter + * + * @return string|null + */ + public function getLocation() + { + return $this->location; + } + + /** + * Set the location of the parameter + * + * @param string|null $location Location of the parameter + * + * @return self + */ + public function setLocation($location) + { + $this->location = $location; + + return $this; + } + + /** + * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being + * applied to a location. + * + * @return string|null + */ + public function getSentAs() + { + return $this->sentAs; + } + + /** + * Set the sentAs attribute + * + * @param string|null $name Name of the value as it is sent over the wire + * + * @return self + */ + public function setSentAs($name) + { + $this->sentAs = $name; + + return $this; + } + + /** + * Retrieve a known property from the parameter by name or a data property by name. When not specific name value + * is specified, all data properties will be returned. + * + * @param string|null $name Specify a particular property name to retrieve + * + * @return array|mixed|null + */ + public function getData($name = null) + { + if (!$name) { + return $this->data; + } + + if (isset($this->data[$name])) { + return $this->data[$name]; + } elseif (isset($this->{$name})) { + return $this->{$name}; + } + + return null; + } + + /** + * Set the extra data properties of the parameter or set a specific extra property + * + * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set + * @param mixed|null $data When setting a specific extra property, specify the data to set for it + * + * @return self + */ + public function setData($nameOrData, $data = null) + { + if (is_array($nameOrData)) { + $this->data = $nameOrData; + } else { + $this->data[$nameOrData] = $data; + } + + return $this; + } + + /** + * Get whether or not the default value can be changed + * + * @return mixed|null + */ + public function getStatic() + { + return $this->static; + } + + /** + * Set to true if the default value cannot be changed + * + * @param bool $static True or false + * + * @return self + */ + public function setStatic($static) + { + $this->static = (bool) $static; + + return $this; + } + + /** + * Get an array of filters used by the parameter + * + * @return array + */ + public function getFilters() + { + return $this->filters ?: array(); + } + + /** + * Set the array of filters used by the parameter + * + * @param array $filters Array of functions to use as filters + * + * @return self + */ + public function setFilters(array $filters) + { + $this->filters = array(); + foreach ($filters as $filter) { + $this->addFilter($filter); + } + + return $this; + } + + /** + * Add a filter to the parameter + * + * @param string|array $filter Method to filter the value through + * + * @return self + * @throws InvalidArgumentException + */ + public function addFilter($filter) + { + if (is_array($filter)) { + if (!isset($filter['method'])) { + throw new InvalidArgumentException('A [method] value must be specified for each complex filter'); + } + } + + if (!$this->filters) { + $this->filters = array($filter); + } else { + $this->filters[] = $filter; + } + + return $this; + } + + /** + * Get the parent object (an {@see OperationInterface} or {@see Parameter} + * + * @return OperationInterface|Parameter|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * Set the parent object of the parameter + * + * @param OperationInterface|Parameter|null $parent Parent container of the parameter + * + * @return self + */ + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Get the properties of the parameter + * + * @return array + */ + public function getProperties() + { + if (!$this->propertiesCache) { + $this->propertiesCache = array(); + foreach (array_keys($this->properties) as $name) { + $this->propertiesCache[$name] = $this->getProperty($name); + } + } + + return $this->propertiesCache; + } + + /** + * Get a specific property from the parameter + * + * @param string $name Name of the property to retrieve + * + * @return null|Parameter + */ + public function getProperty($name) + { + if (!isset($this->properties[$name])) { + return null; + } + + if (!($this->properties[$name] instanceof self)) { + $this->properties[$name]['name'] = $name; + $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription); + $this->properties[$name]->setParent($this); + } + + return $this->properties[$name]; + } + + /** + * Remove a property from the parameter + * + * @param string $name Name of the property to remove + * + * @return self + */ + public function removeProperty($name) + { + unset($this->properties[$name]); + $this->propertiesCache = null; + + return $this; + } + + /** + * Add a property to the parameter + * + * @param Parameter $property Properties to set + * + * @return self + */ + public function addProperty(Parameter $property) + { + $this->properties[$property->getName()] = $property; + $property->setParent($this); + $this->propertiesCache = null; + + return $this; + } + + /** + * Get the additionalProperties value of the parameter + * + * @return bool|Parameter|null + */ + public function getAdditionalProperties() + { + if (is_array($this->additionalProperties)) { + $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription); + $this->additionalProperties->setParent($this); + } + + return $this->additionalProperties; + } + + /** + * Set the additionalProperties value of the parameter + * + * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow + * + * @return self + */ + public function setAdditionalProperties($additional) + { + $this->additionalProperties = $additional; + + return $this; + } + + /** + * Set the items data of the parameter + * + * @param Parameter|null $items Items to set + * + * @return self + */ + public function setItems(Parameter $items = null) + { + if ($this->items = $items) { + $this->items->setParent($this); + } + + return $this; + } + + /** + * Get the item data of the parameter + * + * @return Parameter|null + */ + public function getItems() + { + if (is_array($this->items)) { + $this->items = new static($this->items, $this->serviceDescription); + $this->items->setParent($this); + } + + return $this->items; + } + + /** + * Get the class that the parameter must implement + * + * @return null|string + */ + public function getInstanceOf() + { + return $this->instanceOf; + } + + /** + * Set the class that the parameter must be an instance of + * + * @param string|null $instanceOf Class or interface name + * + * @return self + */ + public function setInstanceOf($instanceOf) + { + $this->instanceOf = $instanceOf; + + return $this; + } + + /** + * Get the enum of strings that are valid for the parameter + * + * @return array|null + */ + public function getEnum() + { + return $this->enum; + } + + /** + * Set the enum of strings that are valid for the parameter + * + * @param array|null $enum Array of strings or null + * + * @return self + */ + public function setEnum(array $enum = null) + { + $this->enum = $enum; + + return $this; + } + + /** + * Get the regex pattern that must match a value when the value is a string + * + * @return string + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Set the regex pattern that must match a value when the value is a string + * + * @param string $pattern Regex pattern + * + * @return self + */ + public function setPattern($pattern) + { + $this->pattern = $pattern; + + return $this; + } + + /** + * Get the format attribute of the schema + * + * @return string + */ + public function getFormat() + { + return $this->format; + } + + /** + * Set the format attribute of the schema + * + * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http) + * + * @return self + */ + public function setFormat($format) + { + $this->format = $format; + + return $this; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php new file mode 100644 index 000000000..7f47fc9d7 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php @@ -0,0 +1,156 @@ +setTimezone(self::getUtcTimeZone())->format($format); + } + + throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object'); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php new file mode 100644 index 000000000..b045422d4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php @@ -0,0 +1,291 @@ +castIntegerToStringType = $castIntegerToStringType; + } + + public function validate(Parameter $param, &$value) + { + $this->errors = array(); + $this->recursiveProcess($param, $value); + + if (empty($this->errors)) { + return true; + } else { + sort($this->errors); + return false; + } + } + + /** + * Get the errors encountered while validating + * + * @return array + */ + public function getErrors() + { + return $this->errors ?: array(); + } + + /** + * Recursively validate a parameter + * + * @param Parameter $param API parameter being validated + * @param mixed $value Value to validate and validate. The value may change during this validate. + * @param string $path Current validation path (used for error reporting) + * @param int $depth Current depth in the validation validate + * + * @return bool Returns true if valid, or false if invalid + */ + protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0) + { + // Update the value by adding default or static values + $value = $param->getValue($value); + + $required = $param->getRequired(); + // if the value is null and the parameter is not required or is static, then skip any further recursion + if ((null === $value && !$required) || $param->getStatic()) { + return true; + } + + $type = $param->getType(); + // Attempt to limit the number of times is_array is called by tracking if the value is an array + $valueIsArray = is_array($value); + // If a name is set then update the path so that validation messages are more helpful + if ($name = $param->getName()) { + $path .= "[{$name}]"; + } + + if ($type == 'object') { + + // Objects are either associative arrays, ToArrayInterface, or some other object + if ($param->getInstanceOf()) { + $instance = $param->getInstanceOf(); + if (!($value instanceof $instance)) { + $this->errors[] = "{$path} must be an instance of {$instance}"; + return false; + } + } + + // Determine whether or not this "value" has properties and should be traversed + $traverse = $temporaryValue = false; + + // Convert the value to an array + if (!$valueIsArray && $value instanceof ToArrayInterface) { + $value = $value->toArray(); + } + + if ($valueIsArray) { + // Ensure that the array is associative and not numerically indexed + if (isset($value[0])) { + $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; + return false; + } + $traverse = true; + } elseif ($value === null) { + // Attempt to let the contents be built up by default values if possible + $value = array(); + $temporaryValue = $valueIsArray = $traverse = true; + } + + if ($traverse) { + + if ($properties = $param->getProperties()) { + // if properties were found, the validate each property of the value + foreach ($properties as $property) { + $name = $property->getName(); + if (isset($value[$name])) { + $this->recursiveProcess($property, $value[$name], $path, $depth + 1); + } else { + $current = null; + $this->recursiveProcess($property, $current, $path, $depth + 1); + // Only set the value if it was populated with something + if (null !== $current) { + $value[$name] = $current; + } + } + } + } + + $additional = $param->getAdditionalProperties(); + if ($additional !== true) { + // If additional properties were found, then validate each against the additionalProperties attr. + $keys = array_keys($value); + // Determine the keys that were specified that were not listed in the properties of the schema + $diff = array_diff($keys, array_keys($properties)); + if (!empty($diff)) { + // Determine which keys are not in the properties + if ($additional instanceOf Parameter) { + foreach ($diff as $key) { + $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); + } + } else { + // if additionalProperties is set to false and there are additionalProperties in the values, then fail + foreach ($diff as $prop) { + $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); + } + } + } + } + + // A temporary value will be used to traverse elements that have no corresponding input value. + // This allows nested required parameters with default values to bubble up into the input. + // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value. + if ($temporaryValue && empty($value)) { + $value = null; + $valueIsArray = false; + } + } + + } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { + foreach ($value as $i => &$item) { + // Validate each item in an array against the items attribute of the schema + $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); + } + } + + // If the value is required and the type is not null, then there is an error if the value is not set + if ($required && $value === null && $type != 'null') { + $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required'); + if ($param->getDescription()) { + $message .= ': ' . $param->getDescription(); + } + $this->errors[] = $message; + return false; + } + + // Validate that the type is correct. If the type is string but an integer was passed, the class can be + // instructed to cast the integer to a string to pass validation. This is the default behavior. + if ($type && (!$type = $this->determineType($type, $value))) { + if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) { + $value = (string) $value; + } else { + $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); + } + } + + // Perform type specific validation for strings, arrays, and integers + if ($type == 'string') { + + // Strings can have enums which are a list of predefined values + if (($enum = $param->getEnum()) && !in_array($value, $enum)) { + $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { + return '"' . addslashes($s) . '"'; + }, $enum)); + } + // Strings can have a regex pattern that the value must match + if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { + $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; + } + + $strLen = null; + if ($min = $param->getMinLength()) { + $strLen = strlen($value); + if ($strLen < $min) { + $this->errors[] = "{$path} length must be greater than or equal to {$min}"; + } + } + if ($max = $param->getMaxLength()) { + if (($strLen ?: strlen($value)) > $max) { + $this->errors[] = "{$path} length must be less than or equal to {$max}"; + } + } + + } elseif ($type == 'array') { + + $size = null; + if ($min = $param->getMinItems()) { + $size = count($value); + if ($size < $min) { + $this->errors[] = "{$path} must contain {$min} or more elements"; + } + } + if ($max = $param->getMaxItems()) { + if (($size ?: count($value)) > $max) { + $this->errors[] = "{$path} must contain {$max} or fewer elements"; + } + } + + } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { + if (($min = $param->getMinimum()) && $value < $min) { + $this->errors[] = "{$path} must be greater than or equal to {$min}"; + } + if (($max = $param->getMaximum()) && $value > $max) { + $this->errors[] = "{$path} must be less than or equal to {$max}"; + } + } + + return empty($this->errors); + } + + /** + * From the allowable types, determine the type that the variable matches + * + * @param string $type Parameter type + * @param mixed $value Value to determine the type + * + * @return string|bool Returns the matching type on + */ + protected function determineType($type, $value) + { + foreach ((array) $type as $t) { + if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) { + return 'string'; + } elseif ($t == 'object' && (is_array($value) || is_object($value))) { + return 'object'; + } elseif ($t == 'array' && is_array($value)) { + return 'array'; + } elseif ($t == 'integer' && is_integer($value)) { + return 'integer'; + } elseif ($t == 'boolean' && is_bool($value)) { + return 'boolean'; + } elseif ($t == 'number' && is_numeric($value)) { + return 'number'; + } elseif ($t == 'numeric' && is_numeric($value)) { + return 'numeric'; + } elseif ($t == 'null' && !$value) { + return 'null'; + } elseif ($t == 'any') { + return 'any'; + } + } + + return false; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php new file mode 100644 index 000000000..286e65eec --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php @@ -0,0 +1,271 @@ +load($config, $options); + } + + /** + * @param array $config Array of configuration data + */ + public function __construct(array $config = array()) + { + $this->fromArray($config); + } + + public function serialize() + { + return json_encode($this->toArray()); + } + + public function unserialize($json) + { + $this->operations = array(); + $this->fromArray(json_decode($json, true)); + } + + public function toArray() + { + $result = array( + 'name' => $this->name, + 'apiVersion' => $this->apiVersion, + 'baseUrl' => $this->baseUrl, + 'description' => $this->description + ) + $this->extraData; + $result['operations'] = array(); + foreach ($this->getOperations() as $name => $operation) { + $result['operations'][$operation->getName() ?: $name] = $operation->toArray(); + } + if (!empty($this->models)) { + $result['models'] = array(); + foreach ($this->models as $id => $model) { + $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model; + } + } + + return array_filter($result); + } + + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Set the baseUrl of the description + * + * @param string $baseUrl Base URL of each operation + * + * @return self + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + + return $this; + } + + public function getOperations() + { + foreach (array_keys($this->operations) as $name) { + $this->getOperation($name); + } + + return $this->operations; + } + + public function hasOperation($name) + { + return isset($this->operations[$name]); + } + + public function getOperation($name) + { + // Lazily retrieve and build operations + if (!isset($this->operations[$name])) { + return null; + } + + if (!($this->operations[$name] instanceof Operation)) { + $this->operations[$name] = new Operation($this->operations[$name], $this); + } + + return $this->operations[$name]; + } + + /** + * Add a operation to the service description + * + * @param OperationInterface $operation Operation to add + * + * @return self + */ + public function addOperation(OperationInterface $operation) + { + $this->operations[$operation->getName()] = $operation->setServiceDescription($this); + + return $this; + } + + public function getModel($id) + { + if (!isset($this->models[$id])) { + return null; + } + + if (!($this->models[$id] instanceof Parameter)) { + $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this); + } + + return $this->models[$id]; + } + + public function getModels() + { + // Ensure all models are converted into parameter objects + foreach (array_keys($this->models) as $id) { + $this->getModel($id); + } + + return $this->models; + } + + public function hasModel($id) + { + return isset($this->models[$id]); + } + + /** + * Add a model to the service description + * + * @param Parameter $model Model to add + * + * @return self + */ + public function addModel(Parameter $model) + { + $this->models[$model->getName()] = $model; + + return $this; + } + + public function getApiVersion() + { + return $this->apiVersion; + } + + public function getName() + { + return $this->name; + } + + public function getDescription() + { + return $this->description; + } + + public function getData($key) + { + return isset($this->extraData[$key]) ? $this->extraData[$key] : null; + } + + public function setData($key, $value) + { + $this->extraData[$key] = $value; + + return $this; + } + + /** + * Initialize the state from an array + * + * @param array $config Configuration data + * @throws InvalidArgumentException + */ + protected function fromArray(array $config) + { + // Keep a list of default keys used in service descriptions that is later used to determine extra data keys + static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description'); + // Pull in the default configuration values + foreach ($defaultKeys as $key) { + if (isset($config[$key])) { + $this->{$key} = $config[$key]; + } + } + + // Account for the Swagger name for Guzzle's baseUrl + if (isset($config['basePath'])) { + $this->baseUrl = $config['basePath']; + } + + // Ensure that the models and operations properties are always arrays + $this->models = (array) $this->models; + $this->operations = (array) $this->operations; + + // We want to add operations differently than adding the other properties + $defaultKeys[] = 'operations'; + + // Create operations for each operation + if (isset($config['operations'])) { + foreach ($config['operations'] as $name => $operation) { + if (!($operation instanceof Operation) && !is_array($operation)) { + throw new InvalidArgumentException('Invalid operation in service description: ' + . gettype($operation)); + } + $this->operations[$name] = $operation; + } + } + + // Get all of the additional properties of the service description and store them in a data array + foreach (array_diff(array_keys($config), $defaultKeys) as $key) { + $this->extraData[$key] = $config[$key]; + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php new file mode 100644 index 000000000..5983e586b --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php @@ -0,0 +1,106 @@ + $op) { + $name = $op['name'] = isset($op['name']) ? $op['name'] : $name; + // Extend other operations + if (!empty($op['extends'])) { + $this->resolveExtension($name, $op, $operations); + } + $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array(); + $operations[$name] = $op; + } + } + + return new ServiceDescription(array( + 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null, + 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null, + 'description' => isset($config['description']) ? $config['description'] : null, + 'operations' => $operations, + 'models' => isset($config['models']) ? $config['models'] : null + ) + $config); + } + + /** + * @param string $name Name of the operation + * @param array $op Operation value array + * @param array $operations Currently loaded operations + * @throws DescriptionBuilderException when extending a non-existent operation + */ + protected function resolveExtension($name, array &$op, array &$operations) + { + $resolved = array(); + $original = empty($op['parameters']) ? false: $op['parameters']; + $hasClass = !empty($op['class']); + foreach ((array) $op['extends'] as $extendedCommand) { + if (empty($operations[$extendedCommand])) { + throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}"); + } + $toArray = $operations[$extendedCommand]; + $resolved = empty($resolved) + ? $toArray['parameters'] + : array_merge($resolved, $toArray['parameters']); + + $op = $op + $toArray; + if (!$hasClass && isset($toArray['class'])) { + $op['class'] = $toArray['class']; + } + } + $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php new file mode 100644 index 000000000..94ca77da4 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php @@ -0,0 +1,28 @@ +getMessage(), $e->getCode(), $e->getPrevious()); + $ce->setSuccessfulRequests($e->getSuccessfulRequests()); + + $alreadyAddedExceptions = array(); + foreach ($e->getFailedRequests() as $request) { + if ($re = $e->getExceptionForFailedRequest($request)) { + $alreadyAddedExceptions[] = $re; + $ce->addFailedRequestWithException($request, $re); + } else { + $ce->addFailedRequest($request); + } + } + + // Add any exceptions that did not map to a request + if (count($alreadyAddedExceptions) < count($e)) { + foreach ($e as $ex) { + if (!in_array($ex, $alreadyAddedExceptions)) { + $ce->add($ex); + } + } + } + + return $ce; + } + + /** + * Get all of the commands in the transfer + * + * @return array + */ + public function getAllCommands() + { + return array_merge($this->successfulCommands, $this->failedCommands); + } + + /** + * Add to the array of successful commands + * + * @param CommandInterface $command Successful command + * + * @return self + */ + public function addSuccessfulCommand(CommandInterface $command) + { + $this->successfulCommands[] = $command; + + return $this; + } + + /** + * Add to the array of failed commands + * + * @param CommandInterface $command Failed command + * + * @return self + */ + public function addFailedCommand(CommandInterface $command) + { + $this->failedCommands[] = $command; + + return $this; + } + + /** + * Get an array of successful commands + * + * @return array + */ + public function getSuccessfulCommands() + { + return $this->successfulCommands; + } + + /** + * Get an array of failed commands + * + * @return array + */ + public function getFailedCommands() + { + return $this->failedCommands; + } + + /** + * Get the Exception that caused the given $command to fail + * + * @param CommandInterface $command Failed command + * + * @return \Exception|null + */ + public function getExceptionForFailedCommand(CommandInterface $command) + { + return $this->getExceptionForFailedRequest($command->getRequest()); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php new file mode 100644 index 000000000..1407e5687 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php @@ -0,0 +1,7 @@ +invalidCommands = $commands; + parent::__construct( + 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . + 'strategy you use with a command transfer must divide command batches by client.' + ); + } + + /** + * Get the invalid commands + * + * @return array + */ + public function getCommands() + { + return $this->invalidCommands; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php new file mode 100644 index 000000000..d59ff2185 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php @@ -0,0 +1,9 @@ +errors = $errors; + } + + /** + * Get any validation errors + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php new file mode 100644 index 000000000..21140e772 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php @@ -0,0 +1,37 @@ +canBuild($command)) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + $className = $this->getClassName($command); + + return new $className($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return (bool) $this->getClassName($command); + } + + /** + * Get the name of the class to instantiate for the command + * + * @param CommandInterface $command Command that is associated with the iterator + * + * @return string + */ + abstract protected function getClassName(CommandInterface $command); +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php new file mode 100644 index 000000000..2efc133c6 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php @@ -0,0 +1,67 @@ +factories = $factories; + } + + public function build(CommandInterface $command, array $options = array()) + { + if (!($factory = $this->getFactory($command))) { + throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); + } + + return $factory->build($command, $options); + } + + public function canBuild(CommandInterface $command) + { + return $this->getFactory($command) !== false; + } + + /** + * Add a factory to the composite factory + * + * @param ResourceIteratorFactoryInterface $factory Factory to add + * + * @return self + */ + public function addFactory(ResourceIteratorFactoryInterface $factory) + { + $this->factories[] = $factory; + + return $this; + } + + /** + * Get the factory that matches the command object + * + * @param CommandInterface $command Command retrieving the iterator for + * + * @return ResourceIteratorFactoryInterface|bool + */ + protected function getFactory(CommandInterface $command) + { + foreach ($this->factories as $factory) { + if ($factory->canBuild($command)) { + return $factory; + } + } + + return false; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php new file mode 100644 index 000000000..c71ca9d85 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php @@ -0,0 +1,34 @@ +map = $map; + } + + public function getClassName(CommandInterface $command) + { + $className = $command->getName(); + + if (isset($this->map[$className])) { + return $this->map[$className]; + } elseif (isset($this->map['*'])) { + // If a wildcard was added, then always use that + return $this->map['*']; + } + + return null; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php new file mode 100644 index 000000000..2322434a5 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php @@ -0,0 +1,64 @@ +data = $data; + $this->structure = $structure; + } + + /** + * Get the structure of the model + * + * @return Parameter + */ + public function getStructure() + { + return $this->structure ?: new Parameter(); + } + + /** + * Provides debug information about the model object + * + * @return string + */ + public function __toString() + { + $output = 'Debug output of '; + if ($this->structure) { + $output .= $this->structure->getName() . ' '; + } + $output .= 'model'; + $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n"; + $output .= "Model data\n-----------\n\n"; + $output .= "This data can be retrieved from the model object using the get() method of the model " + . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n"; + $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1); + $output .= implode("\n", $lines); + + if ($this->structure) { + $output .= "\n\nModel structure\n---------------\n\n"; + $output .= "The following JSON document defines how the model was parsed from an HTTP response into the " + . "associative array structure you see above.\n\n"; + $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n"; + } + + return $output . "\n"; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php new file mode 100644 index 000000000..e14152432 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php @@ -0,0 +1,254 @@ +originalCommand = $command; + + // Parse options from the array of options + $this->data = $data; + $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; + $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; + } + + /** + * Get all of the resources as an array (Warning: this could issue a large number of requests) + * + * @return array + */ + public function toArray() + { + return iterator_to_array($this, false); + } + + public function setLimit($limit) + { + $this->limit = $limit; + $this->resetState(); + + return $this; + } + + public function setPageSize($pageSize) + { + $this->pageSize = $pageSize; + $this->resetState(); + + return $this; + } + + /** + * Get an option from the iterator + * + * @param string $key Key of the option to retrieve + * + * @return mixed|null Returns NULL if not set or the value if set + */ + public function get($key) + { + return array_key_exists($key, $this->data) ? $this->data[$key] : null; + } + + /** + * Set an option on the iterator + * + * @param string $key Key of the option to set + * @param mixed $value Value to set for the option + * + * @return ResourceIterator + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + public function current() + { + return $this->resources ? current($this->resources) : false; + } + + public function key() + { + return max(0, $this->iteratedCount - 1); + } + + public function count() + { + return $this->retrievedCount; + } + + /** + * Get the total number of requests sent + * + * @return int + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * Rewind the Iterator to the first element and send the original command + */ + public function rewind() + { + // Use the original command + $this->command = clone $this->originalCommand; + $this->resetState(); + $this->next(); + } + + public function valid() + { + return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) + && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + public function next() + { + $this->iteratedCount++; + + // Check if a new set of resources needs to be retrieved + $sendRequest = false; + if (!$this->resources) { + $sendRequest = true; + } else { + // iterate over the internal array + $current = next($this->resources); + $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + if ($sendRequest) { + + $this->dispatch('resource_iterator.before_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + + // Get a new command object from the original command + $this->command = clone $this->originalCommand; + // Send a request and retrieve the newly loaded resources + $this->resources = $this->sendRequest(); + $this->requestCount++; + + // If no resources were found, then the last request was not needed + // and iteration must stop + if (empty($this->resources)) { + $this->invalid = true; + } else { + // Add to the number of retrieved resources + $this->retrievedCount += count($this->resources); + // Ensure that we rewind to the beginning of the array + reset($this->resources); + } + + $this->dispatch('resource_iterator.after_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + } + } + + /** + * Retrieve the NextToken that can be used in other iterators. + * + * @return string Returns a NextToken + */ + public function getNextToken() + { + return $this->nextToken; + } + + /** + * Returns the value that should be specified for the page size for a request that will maintain any hard limits, + * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit + * + * @return int Returns the page size of the next request. + */ + protected function calculatePageSize() + { + if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { + return 1 + ($this->limit - $this->iteratedCount); + } + + return (int) $this->pageSize; + } + + /** + * Reset the internal state of the iterator without triggering a rewind() + */ + protected function resetState() + { + $this->iteratedCount = 0; + $this->retrievedCount = 0; + $this->nextToken = false; + $this->resources = null; + $this->invalid = false; + } + + /** + * Send a request to retrieve the next page of results. Hook for subclasses to implement. + * + * @return array Returns the newly loaded resources + */ + abstract protected function sendRequest(); +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php new file mode 100644 index 000000000..6aa36153f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php @@ -0,0 +1,111 @@ +iterator = $iterator; + $this->callback = $callback; + Version::warn(__CLASS__ . ' is deprecated'); + } + + /** + * Apply the callback to the contents of the resource iterator + * + * @param int $perBatch The number of records to group per batch transfer + * + * @return int Returns the number of iterated resources + */ + public function apply($perBatch = 50) + { + $this->iterated = $this->batches = $batches = 0; + $that = $this; + $it = $this->iterator; + $callback = $this->callback; + + $batch = BatchBuilder::factory() + ->createBatchesWith(new BatchSizeDivisor($perBatch)) + ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { + $batches++; + $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); + call_user_func_array($callback, array($it, $batch)); + $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); + })) + ->autoFlushAt($perBatch) + ->build(); + + $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); + + foreach ($this->iterator as $resource) { + $this->iterated++; + $batch->add($resource); + } + + $batch->flush(); + $this->batches = $batches; + + return $this->iterated; + } + + /** + * Get the total number of batches sent + * + * @return int + */ + public function getBatchCount() + { + return $this->batches; + } + + /** + * Get the total number of iterated resources + * + * @return int + */ + public function getIteratedCount() + { + return $this->iterated; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php new file mode 100644 index 000000000..2fd998071 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php @@ -0,0 +1,60 @@ + AbcFoo). + */ +class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory +{ + /** @var array List of namespaces used to look for classes */ + protected $namespaces; + + /** @var InflectorInterface Inflector used to determine class names */ + protected $inflector; + + /** + * @param string|array $namespaces List of namespaces for iterator objects + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct($namespaces = array(), InflectorInterface $inflector = null) + { + $this->namespaces = (array) $namespaces; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * Registers a namespace to check for Iterators + * + * @param string $namespace Namespace which contains Iterator classes + * + * @return self + */ + public function registerNamespace($namespace) + { + array_unshift($this->namespaces, $namespace); + + return $this; + } + + protected function getClassName(CommandInterface $command) + { + $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator'; + + // Determine the name of the class to load + foreach ($this->namespaces as $namespace) { + $potentialClassName = $namespace . '\\' . $iteratorName; + if (class_exists($potentialClassName)) { + return $potentialClassName; + } + } + + return false; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php new file mode 100644 index 000000000..8b4e8dbe0 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php @@ -0,0 +1,30 @@ +=5.3.2", + "guzzle/cache": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version" + }, + "autoload": { + "psr-0": { "Guzzle\\Service": "" } + }, + "target-dir": "Guzzle/Service", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php new file mode 100644 index 000000000..9949e456f --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php @@ -0,0 +1,276 @@ +contextOptions = stream_context_get_options($context); + $this->context = $context; + } elseif (is_array($context) || !$context) { + $this->contextOptions = $context; + $this->createContext($params); + } elseif ($context) { + throw new InvalidArgumentException('$context must be an array or resource'); + } + + // Dispatch the before send event + $request->dispatch('request.before_send', array( + 'request' => $request, + 'context' => $this->context, + 'context_options' => $this->contextOptions + )); + + $this->setUrl($request); + $this->addDefaultContextOptions($request); + $this->addSslOptions($request); + $this->addBodyOptions($request); + $this->addProxyOptions($request); + + // Create the file handle but silence errors + return $this->createStream($params) + ->setCustomData('request', $request) + ->setCustomData('response_headers', $this->getLastResponseHeaders()); + } + + /** + * Set an option on the context and the internal options array + * + * @param string $wrapper Stream wrapper name of http + * @param string $name Context name + * @param mixed $value Context value + * @param bool $overwrite Set to true to overwrite an existing value + */ + protected function setContextValue($wrapper, $name, $value, $overwrite = false) + { + if (!isset($this->contextOptions[$wrapper])) { + $this->contextOptions[$wrapper] = array($name => $value); + } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { + return; + } + $this->contextOptions[$wrapper][$name] = $value; + stream_context_set_option($this->context, $wrapper, $name, $value); + } + + /** + * Create a stream context + * + * @param array $params Parameter array + */ + protected function createContext(array $params) + { + $options = $this->contextOptions; + $this->context = $this->createResource(function () use ($params, $options) { + return stream_context_create($options, $params); + }); + } + + /** + * Get the last response headers received by the HTTP request + * + * @return array + */ + public function getLastResponseHeaders() + { + return $this->lastResponseHeaders; + } + + /** + * Adds the default context options to the stream context options + * + * @param RequestInterface $request Request + */ + protected function addDefaultContextOptions(RequestInterface $request) + { + $this->setContextValue('http', 'method', $request->getMethod()); + $headers = $request->getHeaderLines(); + + // "Connection: close" is required to get streams to work in HTTP 1.1 + if (!$request->hasHeader('Connection')) { + $headers[] = 'Connection: close'; + } + + $this->setContextValue('http', 'header', $headers); + $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion()); + $this->setContextValue('http', 'ignore_errors', true); + } + + /** + * Set the URL to use with the factory + * + * @param RequestInterface $request Request that owns the URL + */ + protected function setUrl(RequestInterface $request) + { + $this->url = $request->getUrl(true); + + // Check for basic Auth username + if ($request->getUsername()) { + $this->url->setUsername($request->getUsername()); + } + + // Check for basic Auth password + if ($request->getPassword()) { + $this->url->setPassword($request->getPassword()); + } + } + + /** + * Add SSL options to the stream context + * + * @param RequestInterface $request Request + */ + protected function addSslOptions(RequestInterface $request) + { + if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { + $this->setContextValue('ssl', 'verify_peer', true, true); + if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { + $this->setContextValue('ssl', 'cafile', $cafile, true); + } + } else { + $this->setContextValue('ssl', 'verify_peer', false, true); + } + } + + /** + * Add body (content) specific options to the context options + * + * @param RequestInterface $request + */ + protected function addBodyOptions(RequestInterface $request) + { + // Add the content for the request if needed + if (!($request instanceof EntityEnclosingRequestInterface)) { + return; + } + + if (count($request->getPostFields())) { + $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); + } elseif ($request->getBody()) { + $this->setContextValue('http', 'content', (string) $request->getBody(), true); + } + + // Always ensure a content-length header is sent + if (isset($this->contextOptions['http']['content'])) { + $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); + $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); + $this->setContextValue('http', 'header', $headers, true); + } + } + + /** + * Add proxy parameters to the context if needed + * + * @param RequestInterface $request Request + */ + protected function addProxyOptions(RequestInterface $request) + { + if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { + $this->setContextValue('http', 'proxy', $proxy); + } + } + + /** + * Create the stream for the request with the context options + * + * @param array $params Parameters of the stream + * + * @return StreamInterface + */ + protected function createStream(array $params) + { + $http_response_header = null; + $url = $this->url; + $context = $this->context; + $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { + return fopen((string) $url, 'r', false, $context); + }); + + // Determine the class to instantiate + $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; + + /** @var $stream StreamInterface */ + $stream = new $className($fp); + + // Track the response headers of the request + if (isset($http_response_header)) { + $this->lastResponseHeaders = $http_response_header; + $this->processResponseHeaders($stream); + } + + return $stream; + } + + /** + * Process response headers + * + * @param StreamInterface $stream + */ + protected function processResponseHeaders(StreamInterface $stream) + { + // Set the size on the stream if it was returned in the response + foreach ($this->lastResponseHeaders as $header) { + if ((stripos($header, 'Content-Length:')) === 0) { + $stream->setSize(trim(substr($header, 15))); + } + } + } + + /** + * Create a resource and check to ensure it was created successfully + * + * @param callable $callback Closure to invoke that must return a valid resource + * + * @return resource + * @throws RuntimeException on error + */ + protected function createResource($callback) + { + // Turn off error reporting while we try to initiate the request + $level = error_reporting(0); + $resource = call_user_func($callback); + error_reporting($level); + + // If the resource could not be created, then grab the last error and throw an exception + if (false === $resource) { + $message = 'Error creating resource. '; + foreach (error_get_last() as $key => $value) { + $message .= "[{$key}] {$value} "; + } + throw new RuntimeException(trim($message)); + } + + return $resource; + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php new file mode 100644 index 000000000..12bed268d --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php @@ -0,0 +1,289 @@ + array( + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true + ), + 'write' => array( + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, + 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, + 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true + ) + ); + + /** + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. + * + * @throws InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + $this->setStream($stream, $size); + } + + /** + * Closes the stream when the helper is destructed + */ + public function __destruct() + { + $this->close(); + } + + public function __toString() + { + if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { + return ''; + } + + $originalPos = $this->ftell(); + $body = stream_get_contents($this->stream, -1, 0); + $this->seek($originalPos); + + return $body; + } + + public function close() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + $this->cache[self::IS_READABLE] = false; + $this->cache[self::IS_WRITABLE] = false; + } + + /** + * Calculate a hash of a Stream + * + * @param StreamInterface $stream Stream to calculate the hash for + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) + * @param bool $rawOutput Whether or not to use raw output + * + * @return bool|string Returns false on failure or a hash string on success + */ + public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) + { + $pos = $stream->ftell(); + if (!$stream->seek(0)) { + return false; + } + + $ctx = hash_init($algo); + while (!$stream->feof()) { + hash_update($ctx, $stream->read(8192)); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $stream->seek($pos); + + return $out; + } + + public function getMetaData($key = null) + { + $meta = stream_get_meta_data($this->stream); + + return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); + } + + public function getStream() + { + return $this->stream; + } + + public function setStream($stream, $size = null) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->rebuildCache(); + + return $this; + } + + public function detachStream() + { + $this->stream = null; + + return $this; + } + + public function getWrapper() + { + return $this->cache[self::WRAPPER_TYPE]; + } + + public function getWrapperData() + { + return $this->getMetaData('wrapper_data') ?: array(); + } + + public function getStreamType() + { + return $this->cache[self::STREAM_TYPE]; + } + + public function getUri() + { + return $this->cache['uri']; + } + + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then use fstat + clearstatcache(true, $this->cache['uri']); + $stats = fstat($this->stream); + if (isset($stats['size'])) { + $this->size = $stats['size']; + return $this->size; + } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { + // Only get the size based on the content if the the stream is readable and seekable + $pos = $this->ftell(); + $this->size = strlen((string) $this); + $this->seek($pos); + return $this->size; + } + + return false; + } + + public function isReadable() + { + return $this->cache[self::IS_READABLE]; + } + + public function isRepeatable() + { + return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; + } + + public function isWritable() + { + return $this->cache[self::IS_WRITABLE]; + } + + public function isConsumed() + { + return feof($this->stream); + } + + public function feof() + { + return $this->isConsumed(); + } + + public function isLocal() + { + return $this->cache[self::IS_LOCAL]; + } + + public function isSeekable() + { + return $this->cache[self::SEEKABLE]; + } + + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + public function seek($offset, $whence = SEEK_SET) + { + return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; + } + + public function read($length) + { + return fread($this->stream, $length); + } + + public function write($string) + { + // We can't know the size after writing anything + $this->size = null; + + return fwrite($this->stream, $string); + } + + public function ftell() + { + return ftell($this->stream); + } + + public function rewind() + { + return $this->seek(0); + } + + public function readLine($maxLength = null) + { + if (!$this->cache[self::IS_READABLE]) { + return false; + } else { + return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); + } + } + + public function setCustomData($key, $value) + { + $this->customData[$key] = $value; + + return $this; + } + + public function getCustomData($key) + { + return isset($this->customData[$key]) ? $this->customData[$key] : null; + } + + /** + * Reprocess stream metadata + */ + protected function rebuildCache() + { + $this->cache = stream_get_meta_data($this->stream); + $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); + $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); + $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); + } +} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php new file mode 100644 index 000000000..6d7dc3761 --- /dev/null +++ b/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php @@ -0,0 +1,218 @@ +=5.3.2", + "guzzle/common": "self.version" + }, + "suggest": { + "guzzle/http": "To convert Guzzle request objects to PHP streams" + }, + "autoload": { + "psr-0": { "Guzzle\\Stream": "" } + }, + "target-dir": "Guzzle/Stream", + "extra": { + "branch-alias": { + "dev-master": "3.7-dev" + } + } +} diff --git a/core/lib/pimple/pimple/.gitignore b/core/lib/pimple/pimple/.gitignore new file mode 100644 index 000000000..ce3aa6521 --- /dev/null +++ b/core/lib/pimple/pimple/.gitignore @@ -0,0 +1 @@ +phpunit.xml diff --git a/core/lib/pimple/pimple/.travis.yml b/core/lib/pimple/pimple/.travis.yml new file mode 100644 index 000000000..72b4067cd --- /dev/null +++ b/core/lib/pimple/pimple/.travis.yml @@ -0,0 +1,6 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 diff --git a/core/lib/pimple/pimple/LICENSE b/core/lib/pimple/pimple/LICENSE new file mode 100644 index 000000000..f61e90d96 --- /dev/null +++ b/core/lib/pimple/pimple/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2009-2013 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/pimple/pimple/README.rst b/core/lib/pimple/pimple/README.rst new file mode 100644 index 000000000..f685caa62 --- /dev/null +++ b/core/lib/pimple/pimple/README.rst @@ -0,0 +1,159 @@ +Pimple +====== + +Pimple is a small Dependency Injection Container for PHP 5.3 that consists +of just one file and one class (about 80 lines of code). + +`Download it`_, require it in your code, and you're good to go:: + + require_once '/path/to/Pimple.php'; + +Creating a container is a matter of instating the ``Pimple`` class:: + + $container = new Pimple(); + +As many other dependency injection containers, Pimple is able to manage two +different kind of data: *services* and *parameters*. + +Defining Parameters +------------------- + +Defining a parameter is as simple as using the Pimple instance as an array:: + + // define some parameters + $container['cookie_name'] = 'SESSION_ID'; + $container['session_storage_class'] = 'SessionStorage'; + +Defining Services +----------------- + +A service is an object that does something as part of a larger system. +Examples of services: Database connection, templating engine, mailer. Almost +any object could be a service. + +Services are defined by anonymous functions that return an instance of an +object:: + + // define some services + $container['session_storage'] = function ($c) { + return new $c['session_storage_class']($c['cookie_name']); + }; + + $container['session'] = function ($c) { + return new Session($c['session_storage']); + }; + +Notice that the anonymous function has access to the current container +instance, allowing references to other services or parameters. + +As objects are only created when you get them, the order of the definitions +does not matter, and there is no performance penalty. + +Using the defined services is also very easy:: + + // get the session object + $session = $container['session']; + + // the above call is roughly equivalent to the following code: + // $storage = new SessionStorage('SESSION_ID'); + // $session = new Session($storage); + +Defining Shared Services +------------------------ + +By default, each time you get a service, Pimple returns a new instance of it. +If you want the same instance to be returned for all calls, wrap your +anonymous function with the ``share()`` method:: + + $container['session'] = $container->share(function ($c) { + return new Session($c['session_storage']); + }); + +Protecting Parameters +--------------------- + +Because Pimple sees anonymous functions as service definitions, you need to +wrap anonymous functions with the ``protect()`` method to store them as +parameter:: + + $container['random'] = $container->protect(function () { return rand(); }); + +Modifying services after creation +--------------------------------- + +In some cases you may want to modify a service definition after it has been +defined. You can use the ``extend()`` method to define additional code to +be run on your service just after it is created:: + + $container['mail'] = function ($c) { + return new \Zend_Mail(); + }; + + $container['mail'] = $container->extend('mail', function($mail, $c) { + $mail->setFrom($c['mail.default_from']); + return $mail; + }); + +The first argument is the name of the object, the second is a function that +gets access to the object instance and the container. The return value is +a service definition, so you need to re-assign it on the container. + +If the service you plan to extend is already shared, it's recommended that you +re-wrap your extended service with the ``shared`` method, otherwise your extension +code will be called every time you access the service:: + + $container['twig'] = $container->share(function ($c) { + return new Twig_Environment($c['twig.loader'], $c['twig.options']); + }); + + $container['twig'] = $container->share($container->extend('twig', function ($twig, $c) { + $twig->addExtension(new MyTwigExtension()); + return $twig; + })); + +Fetching the service creation function +-------------------------------------- + +When you access an object, Pimple automatically calls the anonymous function +that you defined, which creates the service object for you. If you want to get +raw access to this function, you can use the ``raw()`` method:: + + $container['session'] = $container->share(function ($c) { + return new Session($c['session_storage']); + }); + + $sessionFunction = $container->raw('session'); + +Packaging a Container for reusability +------------------------------------- + +If you use the same libraries over and over, you might want to create reusable +containers. Creating a reusable container is as simple as creating a class +that extends ``Pimple``, and configuring it in the constructor:: + + class SomeContainer extends Pimple + { + public function __construct() + { + $this['parameter'] = 'foo'; + $this['object'] = function () { return stdClass(); }; + } + } + +Using this container from your own is as easy as it can get:: + + $container = new Pimple(); + + // define your project parameters and services + // ... + + // embed the SomeContainer container + $container['embedded'] = $container->share(function () { return new SomeContainer(); }); + + // configure it + $container['embedded']['parameter'] = 'bar'; + + // use it + $container['embedded']['object']->...; + +.. _Download it: https://github.com/fabpot/Pimple diff --git a/core/lib/pimple/pimple/composer.json b/core/lib/pimple/pimple/composer.json new file mode 100644 index 000000000..d95c8c521 --- /dev/null +++ b/core/lib/pimple/pimple/composer.json @@ -0,0 +1,25 @@ +{ + "name": "pimple/pimple", + "type": "library", + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "keywords": ["dependency injection", "container"], + "homepage": "http://pimple.sensiolabs.org", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "Pimple": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + } +} \ No newline at end of file diff --git a/core/lib/pimple/pimple/lib/Pimple.php b/core/lib/pimple/pimple/lib/Pimple.php new file mode 100644 index 000000000..b980522d5 --- /dev/null +++ b/core/lib/pimple/pimple/lib/Pimple.php @@ -0,0 +1,214 @@ +values = $values; + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same a name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to defined an object + */ + public function offsetSet($id, $value) + { + $this->values[$id] = $value; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws InvalidArgumentException if the identifier is not defined + */ + public function offsetGet($id) + { + if (!array_key_exists($id, $this->values)) { + throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + $isFactory = is_object($this->values[$id]) && method_exists($this->values[$id], '__invoke'); + + return $isFactory ? $this->values[$id]($this) : $this->values[$id]; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return Boolean + */ + public function offsetExists($id) + { + return array_key_exists($id, $this->values); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + unset($this->values[$id]); + } + + /** + * Returns a closure that stores the result of the given service definition + * for uniqueness in the scope of this instance of Pimple. + * + * @param callable $callable A service definition to wrap for uniqueness + * + * @return Closure The wrapped closure + */ + public static function share($callable) + { + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new InvalidArgumentException('Service definition is not a Closure or invokable object.'); + } + + return function ($c) use ($callable) { + static $object; + + if (null === $object) { + $object = $callable($c); + } + + return $object; + }; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return Closure The protected closure + */ + public static function protect($callable) + { + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new InvalidArgumentException('Callable is not a Closure or invokable object.'); + } + + return function ($c) use ($callable) { + return $callable; + }; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws InvalidArgumentException if the identifier is not defined + */ + public function raw($id) + { + if (!array_key_exists($id, $this->values)) { + throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return Closure The wrapped closure + * + * @throws InvalidArgumentException if the identifier is not defined or not a service definition + */ + public function extend($id, $callable) + { + if (!array_key_exists($id, $this->values)) { + throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); + } + + if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { + throw new InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); + } + + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + return $this->values[$id] = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return array_keys($this->values); + } +} diff --git a/core/lib/pimple/pimple/phpunit.xml.dist b/core/lib/pimple/pimple/phpunit.xml.dist new file mode 100644 index 000000000..9de2c19e9 --- /dev/null +++ b/core/lib/pimple/pimple/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + + ./tests/Pimple/ + + + diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md new file mode 100644 index 000000000..536c5ac72 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md @@ -0,0 +1,16 @@ +CHANGELOG +========= + +2.1.0 +----- + + * added TraceableEventDispatcherInterface + * added ContainerAwareEventDispatcher + * added a reference to the EventDispatcher on the Event + * added a reference to the Event name on the event + * added fluid interface to the dispatch() method which now returns the Event + object + * added GenericEvent event class + * added the possibility for subscribers to subscribe several times for the + same event + * added ImmutableEventDispatcher diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php new file mode 100644 index 000000000..e97d427ea --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Lazily loads listeners and subscribers from the dependency injection + * container + * + * @author Fabien Potencier + * @author Bernhard Schussek + * @author Jordan Alliot + */ +class ContainerAwareEventDispatcher extends EventDispatcher +{ + /** + * The container from where services are loaded + * @var ContainerInterface + */ + private $container; + + /** + * The service IDs of the event listeners and subscribers + * @var array + */ + private $listenerIds = array(); + + /** + * The services registered as listeners + * @var array + */ + private $listeners = array(); + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * Adds a service as event listener + * + * @param string $eventName Event for which the listener is added + * @param array $callback The service ID of the listener service & the method + * name that has to be called + * @param int $priority The higher this value, the earlier an event listener + * will be triggered in the chain. + * Defaults to 0. + * + * @throws \InvalidArgumentException + */ + public function addListenerService($eventName, $callback, $priority = 0) + { + if (!is_array($callback) || 2 !== count($callback)) { + throw new \InvalidArgumentException('Expected an array("service", "method") argument'); + } + + $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); + } + + public function removeListener($eventName, $listener) + { + $this->lazyLoad($eventName); + + if (isset($this->listeners[$eventName])) { + foreach ($this->listeners[$eventName] as $key => $l) { + foreach ($this->listenerIds[$eventName] as $i => $args) { + list($serviceId, $method, $priority) = $args; + if ($key === $serviceId.'.'.$method) { + if ($listener === array($l, $method)) { + unset($this->listeners[$eventName][$key]); + if (empty($this->listeners[$eventName])) { + unset($this->listeners[$eventName]); + } + unset($this->listenerIds[$eventName][$i]); + if (empty($this->listenerIds[$eventName])) { + unset($this->listenerIds[$eventName]); + } + } + } + } + } + } + + parent::removeListener($eventName, $listener); + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + if (null === $eventName) { + return (bool) count($this->listenerIds) || (bool) count($this->listeners); + } + + if (isset($this->listenerIds[$eventName])) { + return true; + } + + return parent::hasListeners($eventName); + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null === $eventName) { + foreach (array_keys($this->listenerIds) as $serviceEventName) { + $this->lazyLoad($serviceEventName); + } + } else { + $this->lazyLoad($eventName); + } + + return parent::getListeners($eventName); + } + + /** + * Adds a service as event subscriber + * + * @param string $serviceId The service ID of the subscriber service + * @param string $class The service's class name (which must implement EventSubscriberInterface) + */ + public function addSubscriberService($serviceId, $class) + { + foreach ($class::getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->listenerIds[$eventName][] = array($serviceId, $params, 0); + } elseif (is_string($params[0])) { + $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * {@inheritdoc} + * + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @throws \InvalidArgumentException if the service is not defined + */ + public function dispatch($eventName, Event $event = null) + { + $this->lazyLoad($eventName); + + return parent::dispatch($eventName, $event); + } + + public function getContainer() + { + return $this->container; + } + + /** + * Lazily loads listeners for this event from the dependency injection + * container. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + */ + protected function lazyLoad($eventName) + { + if (isset($this->listenerIds[$eventName])) { + foreach ($this->listenerIds[$eventName] as $args) { + list($serviceId, $method, $priority) = $args; + $listener = $this->container->get($serviceId); + + $key = $serviceId.'.'.$method; + if (!isset($this->listeners[$eventName][$key])) { + $this->addListener($eventName, array($listener, $method), $priority); + } elseif ($listener !== $this->listeners[$eventName][$key]) { + parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); + $this->addListener($eventName, array($listener, $method), $priority); + } + + $this->listeners[$eventName][$key] = $listener; + } + } + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 000000000..a67a97901 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Debug; + +/** + * @author Fabien Potencier + */ +interface TraceableEventDispatcherInterface +{ + /** + * Gets the called listeners. + * + * @return array An array of called listeners + */ + public function getCalledListeners(); + + /** + * Gets the not called listeners. + * + * @return array An array of not called listeners + */ + public function getNotCalledListeners(); +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php new file mode 100644 index 000000000..bf792a257 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event is the base class for classes containing event data. + * + * This class contains no event data. It is used by events that do not pass + * state information to an event handler when an event is raised. + * + * You can call the method stopPropagation() to abort the execution of + * further listeners in your event listener. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +class Event +{ + /** + * @var bool Whether no further event listeners should be triggered + */ + private $propagationStopped = false; + + /** + * @var EventDispatcher Dispatcher that dispatched this event + */ + private $dispatcher; + + /** + * @var string This event's name + */ + private $name; + + /** + * Returns whether further event listeners should be triggered. + * + * @see Event::stopPropagation + * @return bool Whether propagation was already stopped for this event. + * + * @api + */ + public function isPropagationStopped() + { + return $this->propagationStopped; + } + + /** + * Stops the propagation of the event to further event listeners. + * + * If multiple event listeners are connected to the same event, no + * further event listener will be triggered once any trigger calls + * stopPropagation(). + * + * @api + */ + public function stopPropagation() + { + $this->propagationStopped = true; + } + + /** + * Stores the EventDispatcher that dispatches this Event + * + * @param EventDispatcherInterface $dispatcher + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function setDispatcher(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * Returns the EventDispatcher that dispatches this Event + * + * @return EventDispatcherInterface + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * + * @api + */ + public function getDispatcher() + { + return $this->dispatcher; + } + + /** + * Gets the event's name. + * + * @return string + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the event's name property. + * + * @param string $name The event name. + * + * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php new file mode 100644 index 000000000..ab140a669 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -0,0 +1,185 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * @author Fabien Potencier + * @author Jordi Boggiano + * @author Jordan Alliot + * + * @api + */ +class EventDispatcher implements EventDispatcherInterface +{ + private $listeners = array(); + private $sorted = array(); + + /** + * @see EventDispatcherInterface::dispatch + * + * @api + */ + public function dispatch($eventName, Event $event = null) + { + if (null === $event) { + $event = new Event(); + } + + $event->setDispatcher($this); + $event->setName($eventName); + + if (!isset($this->listeners[$eventName])) { + return $event; + } + + $this->doDispatch($this->getListeners($eventName), $eventName, $event); + + return $event; + } + + /** + * @see EventDispatcherInterface::getListeners + */ + public function getListeners($eventName = null) + { + if (null !== $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + + return $this->sorted[$eventName]; + } + + foreach (array_keys($this->listeners) as $eventName) { + if (!isset($this->sorted[$eventName])) { + $this->sortListeners($eventName); + } + } + + return $this->sorted; + } + + /** + * @see EventDispatcherInterface::hasListeners + */ + public function hasListeners($eventName = null) + { + return (bool) count($this->getListeners($eventName)); + } + + /** + * @see EventDispatcherInterface::addListener + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0) + { + $this->listeners[$eventName][$priority][] = $listener; + unset($this->sorted[$eventName]); + } + + /** + * @see EventDispatcherInterface::removeListener + */ + public function removeListener($eventName, $listener) + { + if (!isset($this->listeners[$eventName])) { + return; + } + + foreach ($this->listeners[$eventName] as $priority => $listeners) { + if (false !== ($key = array_search($listener, $listeners, true))) { + unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); + } + } + } + + /** + * @see EventDispatcherInterface::addSubscriber + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_string($params)) { + $this->addListener($eventName, array($subscriber, $params)); + } elseif (is_string($params[0])) { + $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); + } else { + foreach ($params as $listener) { + $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); + } + } + } + } + + /** + * @see EventDispatcherInterface::removeSubscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { + if (is_array($params) && is_array($params[0])) { + foreach ($params as $listener) { + $this->removeListener($eventName, array($subscriber, $listener[0])); + } + } else { + $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); + } + } + } + + /** + * Triggers the listeners of an event. + * + * This method can be overridden to add functionality that is executed + * for each listener. + * + * @param callable[] $listeners The event listeners. + * @param string $eventName The name of the event to dispatch. + * @param Event $event The event object to pass to the event handlers/listeners. + */ + protected function doDispatch($listeners, $eventName, Event $event) + { + foreach ($listeners as $listener) { + call_user_func($listener, $event, $eventName, $this); + if ($event->isPropagationStopped()) { + break; + } + } + } + + /** + * Sorts the internal list of listeners for the given event by priority. + * + * @param string $eventName The name of the event. + */ + private function sortListeners($eventName) + { + $this->sorted[$eventName] = array(); + + if (isset($this->listeners[$eventName])) { + krsort($this->listeners[$eventName]); + $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); + } + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php new file mode 100644 index 000000000..3fdbfd882 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -0,0 +1,96 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * The EventDispatcherInterface is the central point of Symfony's event listener system. + * Listeners are registered on the manager and events are dispatched through the + * manager. + * + * @author Bernhard Schussek + * + * @api + */ +interface EventDispatcherInterface +{ + /** + * Dispatches an event to all registered listeners. + * + * @param string $eventName The name of the event to dispatch. The name of + * the event is the name of the method that is + * invoked on listeners. + * @param Event $event The event to pass to the event handlers/listeners. + * If not supplied, an empty Event instance is created. + * + * @return Event + * + * @api + */ + public function dispatch($eventName, Event $event = null); + + /** + * Adds an event listener that listens on the specified events. + * + * @param string $eventName The event to listen on + * @param callable $listener The listener + * @param int $priority The higher this value, the earlier an event + * listener will be triggered in the chain (defaults to 0) + * + * @api + */ + public function addListener($eventName, $listener, $priority = 0); + + /** + * Adds an event subscriber. + * + * The subscriber is asked for all the events he is + * interested in and added as a listener for these events. + * + * @param EventSubscriberInterface $subscriber The subscriber. + * + * @api + */ + public function addSubscriber(EventSubscriberInterface $subscriber); + + /** + * Removes an event listener from the specified events. + * + * @param string|array $eventName The event(s) to remove a listener from + * @param callable $listener The listener to remove + */ + public function removeListener($eventName, $listener); + + /** + * Removes an event subscriber. + * + * @param EventSubscriberInterface $subscriber The subscriber + */ + public function removeSubscriber(EventSubscriberInterface $subscriber); + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string $eventName The name of the event + * + * @return array The event listeners for the specified event, or all event listeners by event name + */ + public function getListeners($eventName = null); + + /** + * Checks whether an event has any registered listeners. + * + * @param string $eventName The name of the event + * + * @return bool true if the specified event has any listeners, false otherwise + */ + public function hasListeners($eventName = null); +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php new file mode 100644 index 000000000..080f892fd --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * An EventSubscriber knows himself what events he is interested in. + * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes + * {@link getSubscribedEvents} and registers the subscriber as a listener for all + * returned events. + * + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Bernhard Schussek + * + * @api + */ +interface EventSubscriberInterface +{ + /** + * Returns an array of event names this subscriber wants to listen to. + * + * The array keys are event names and the value can be: + * + * * The method name to call (priority defaults to 0) + * * An array composed of the method name to call and the priority + * * An array of arrays composed of the method names to call and respective + * priorities, or 0 if unset + * + * For instance: + * + * * array('eventName' => 'methodName') + * * array('eventName' => array('methodName', $priority)) + * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) + * + * @return array The event names to listen to + * + * @api + */ + public static function getSubscribedEvents(); +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php new file mode 100644 index 000000000..1e8c44a67 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php @@ -0,0 +1,186 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * Event encapsulation class. + * + * Encapsulates events thus decoupling the observer from the subject they encapsulate. + * + * @author Drak + */ +class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate +{ + /** + * Event subject. + * + * @var mixed usually object or callable + */ + protected $subject; + + /** + * Array of arguments. + * + * @var array + */ + protected $arguments; + + /** + * Encapsulate an event with $subject and $args. + * + * @param mixed $subject The subject of the event, usually an object. + * @param array $arguments Arguments to store in the event. + */ + public function __construct($subject = null, array $arguments = array()) + { + $this->subject = $subject; + $this->arguments = $arguments; + } + + /** + * Getter for subject property. + * + * @return mixed $subject The observer subject. + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Get argument by key. + * + * @param string $key Key. + * + * @throws \InvalidArgumentException If key is not found. + * + * @return mixed Contents of array key. + */ + public function getArgument($key) + { + if ($this->hasArgument($key)) { + return $this->arguments[$key]; + } + + throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + } + + /** + * Add argument to event. + * + * @param string $key Argument name. + * @param mixed $value Value. + * + * @return GenericEvent + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Getter for all arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Set args property. + * + * @param array $args Arguments. + * + * @return GenericEvent + */ + public function setArguments(array $args = array()) + { + $this->arguments = $args; + + return $this; + } + + /** + * Has argument. + * + * @param string $key Key of arguments array. + * + * @return bool + */ + public function hasArgument($key) + { + return array_key_exists($key, $this->arguments); + } + + /** + * ArrayAccess for argument getter. + * + * @param string $key Array key. + * + * @throws \InvalidArgumentException If key does not exist in $this->args. + * + * @return mixed + */ + public function offsetGet($key) + { + return $this->getArgument($key); + } + + /** + * ArrayAccess for argument setter. + * + * @param string $key Array key to set. + * @param mixed $value Value. + */ + public function offsetSet($key, $value) + { + $this->setArgument($key, $value); + } + + /** + * ArrayAccess for unset argument. + * + * @param string $key Array key. + */ + public function offsetUnset($key) + { + if ($this->hasArgument($key)) { + unset($this->arguments[$key]); + } + } + + /** + * ArrayAccess has argument. + * + * @param string $key Array key. + * + * @return bool + */ + public function offsetExists($key) + { + return $this->hasArgument($key); + } + + /** + * IteratorAggregate for iterating over the object like an array + * + * @return \ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->arguments); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php new file mode 100644 index 000000000..b70b81a8b --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher; + +/** + * A read-only proxy for an event dispatcher. + * + * @author Bernhard Schussek + */ +class ImmutableEventDispatcher implements EventDispatcherInterface +{ + /** + * The proxied dispatcher. + * @var EventDispatcherInterface + */ + private $dispatcher; + + /** + * Creates an unmodifiable proxy for an event dispatcher. + * + * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. + */ + public function __construct(EventDispatcherInterface $dispatcher) + { + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, Event $event = null) + { + return $this->dispatcher->dispatch($eventName, $event); + } + + /** + * {@inheritdoc} + */ + public function addListener($eventName, $listener, $priority = 0) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeListener($eventName, $listener) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function removeSubscriber(EventSubscriberInterface $subscriber) + { + throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); + } + + /** + * {@inheritdoc} + */ + public function getListeners($eventName = null) + { + return $this->dispatcher->getListeners($eventName); + } + + /** + * {@inheritdoc} + */ + public function hasListeners($eventName = null) + { + return $this->dispatcher->hasListeners($eventName); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md new file mode 100644 index 000000000..22bf74fdc --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md @@ -0,0 +1,25 @@ +EventDispatcher Component +========================= + +The Symfony2 EventDispatcher component implements the Mediator pattern in a +simple and effective way to make your projects truly extensible. + + use Symfony\Component\EventDispatcher\EventDispatcher; + use Symfony\Component\EventDispatcher\Event; + + $dispatcher = new EventDispatcher(); + + $dispatcher->addListener('event_name', function (Event $event) { + // ... + }); + + $dispatcher->dispatch('event_name'); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/EventDispatcher/ + $ composer.phar install + $ phpunit diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php new file mode 100644 index 000000000..fb3b4caa2 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php @@ -0,0 +1,244 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\Scope; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + public function testAddAListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testAddASubscriberService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.subscriber', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); + + $dispatcher->dispatch('onEvent', $event); + } + + public function testPreventDuplicateListenerService() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); + + $dispatcher->dispatch('onEvent', $event); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testTriggerAListenerServiceOutOfScope() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $container->leaveScope('scope'); + $dispatcher->dispatch('onEvent'); + } + + public function testReEnteringAScope() + { + $event = new Event(); + + $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service1 + ->expects($this->exactly(2)) + ->method('onEvent') + ->with($event) + ; + + $scope = new Scope('scope'); + $container = new Container(); + $container->addScope($scope); + $container->enterScope('scope'); + + $container->set('service.listener', $service1, 'scope'); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + $dispatcher->dispatch('onEvent', $event); + + $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $service2 + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $container->enterScope('scope'); + $container->set('service.listener', $service2, 'scope'); + + $dispatcher->dispatch('onEvent', $event); + + $container->leaveScope('scope'); + + $dispatcher->dispatch('onEvent'); + } + + public function testHasListenersOnLazyLoad() + { + $event = new Event(); + + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $event->setDispatcher($dispatcher); + $event->setName('onEvent'); + + $service + ->expects($this->once()) + ->method('onEvent') + ->with($event) + ; + + $this->assertTrue($dispatcher->hasListeners()); + + if ($dispatcher->hasListeners('onEvent')) { + $dispatcher->dispatch('onEvent'); + } + } + + public function testGetListenersOnLazyLoad() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $listeners = $dispatcher->getListeners(); + + $this->assertTrue(isset($listeners['onEvent'])); + + $this->assertCount(1, $dispatcher->getListeners('onEvent')); + } + + public function testRemoveAfterDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->dispatch('onEvent', new Event()); + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } + + public function testRemoveBeforeDispatch() + { + $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); + + $container = new Container(); + $container->set('service.listener', $service); + + $dispatcher = new ContainerAwareEventDispatcher($container); + $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); + + $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); + $this->assertFalse($dispatcher->hasListeners('onEvent')); + } +} + +class Service +{ + public function onEvent(Event $e) + { + } +} + +class SubscriberService implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'onEvent' => 'onEvent', + 'onEvent' => array('onEvent', 10), + 'onEvent' => array('onEvent'), + ); + } + + public function onEvent(Event $e) + { + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 000000000..1e3282f8d --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php @@ -0,0 +1,346 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class EventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /* Some pseudo events */ + const preFoo = 'pre.foo'; + const postFoo = 'post.foo'; + const preBar = 'pre.bar'; + const postBar = 'post.bar'; + + /** + * @var EventDispatcher + */ + private $dispatcher; + + private $listener; + + protected function setUp() + { + $this->dispatcher = new EventDispatcher(); + $this->listener = new TestEventListener(); + } + + protected function tearDown() + { + $this->dispatcher = null; + $this->listener = null; + } + + public function testInitialState() + { + $this->assertEquals(array(), $this->dispatcher->getListeners()); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddListener() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); + $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); + $this->assertCount(2, $this->dispatcher->getListeners()); + } + + public function testGetListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener1->name = '1'; + $listener2->name = '2'; + $listener3->name = '3'; + + $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); + $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); + $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); + + $expected = array( + array($listener2, 'preFoo'), + array($listener3, 'preFoo'), + array($listener1, 'preFoo'), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); + } + + public function testGetAllListenersSortsByPriority() + { + $listener1 = new TestEventListener(); + $listener2 = new TestEventListener(); + $listener3 = new TestEventListener(); + $listener4 = new TestEventListener(); + $listener5 = new TestEventListener(); + $listener6 = new TestEventListener(); + + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->addListener('post.foo', $listener4, -10); + $this->dispatcher->addListener('post.foo', $listener5); + $this->dispatcher->addListener('post.foo', $listener6, 10); + + $expected = array( + 'pre.foo' => array($listener3, $listener2, $listener1), + 'post.foo' => array($listener6, $listener5, $listener4), + ); + + $this->assertSame($expected, $this->dispatcher->getListeners()); + } + + public function testDispatch() + { + $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); + $this->dispatcher->dispatch(self::preFoo); + $this->assertTrue($this->listener->preFooInvoked); + $this->assertFalse($this->listener->postFooInvoked); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + $this->assertSame($event, $return); + } + + public function testDispatchForClosure() + { + $invoked = 0; + $listener = function () use (&$invoked) { + $invoked++; + }; + $this->dispatcher->addListener('pre.foo', $listener); + $this->dispatcher->addListener('post.foo', $listener); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(1, $invoked); + } + + public function testStopEventPropagation() + { + $otherListener = new TestEventListener(); + + // postFoo() stops the propagation, so only one listener should + // be executed + // Manually set priority to enforce $this->listener to be called first + $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); + $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); + $this->dispatcher->dispatch(self::postFoo); + $this->assertTrue($this->listener->postFooInvoked); + $this->assertFalse($otherListener->postFooInvoked); + } + + public function testDispatchByPriority() + { + $invoked = array(); + $listener1 = function () use (&$invoked) { + $invoked[] = '1'; + }; + $listener2 = function () use (&$invoked) { + $invoked[] = '2'; + }; + $listener3 = function () use (&$invoked) { + $invoked[] = '3'; + }; + $this->dispatcher->addListener('pre.foo', $listener1, -10); + $this->dispatcher->addListener('pre.foo', $listener2); + $this->dispatcher->addListener('pre.foo', $listener3, 10); + $this->dispatcher->dispatch(self::preFoo); + $this->assertEquals(array('3', '2', '1'), $invoked); + } + + public function testRemoveListener() + { + $this->dispatcher->addListener('pre.bar', $this->listener); + $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('pre.bar', $this->listener); + $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); + $this->dispatcher->removeListener('notExists', $this->listener); + } + + public function testAddSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testAddSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); + } + + public function testAddSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + + $listeners = $this->dispatcher->getListeners('pre.foo'); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $listeners); + $this->assertEquals('preFoo2', $listeners[0][1]); + } + + public function testRemoveSubscriber() + { + $eventSubscriber = new TestEventSubscriber(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); + } + + public function testRemoveSubscriberWithPriorities() + { + $eventSubscriber = new TestEventSubscriberWithPriorities(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testRemoveSubscriberWithMultipleListeners() + { + $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); + $this->dispatcher->addSubscriber($eventSubscriber); + $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); + $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); + $this->dispatcher->removeSubscriber($eventSubscriber); + $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); + } + + public function testEventReceivesTheDispatcherInstance() + { + $dispatcher = null; + $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { + $dispatcher = $event->getDispatcher(); + }); + $this->dispatcher->dispatch('test'); + $this->assertSame($this->dispatcher, $dispatcher); + } + + public function testEventReceivesTheDispatcherInstanceAsArgument() + { + $listener = new TestWithDispatcher(); + $this->dispatcher->addListener('test', array($listener, 'foo')); + $this->assertNull($listener->name); + $this->assertNull($listener->dispatcher); + $this->dispatcher->dispatch('test'); + $this->assertEquals('test', $listener->name); + $this->assertSame($this->dispatcher, $listener->dispatcher); + } + + /** + * @see https://bugs.php.net/bug.php?id=62976 + * + * This bug affects: + * - The PHP 5.3 branch for versions < 5.3.18 + * - The PHP 5.4 branch for versions < 5.4.8 + * - The PHP 5.5 branch is not affected + */ + public function testWorkaroundForPhpBug62976() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener('bug.62976', new CallableClass()); + $dispatcher->removeListener('bug.62976', function () {}); + $this->assertTrue($dispatcher->hasListeners('bug.62976')); + } +} + +class CallableClass +{ + public function __invoke() + { + } +} + +class TestEventListener +{ + public $preFooInvoked = false; + public $postFooInvoked = false; + + /* Listener methods */ + + public function preFoo(Event $e) + { + $this->preFooInvoked = true; + } + + public function postFoo(Event $e) + { + $this->postFooInvoked = true; + + $e->stopPropagation(); + } +} + +class TestWithDispatcher +{ + public $name; + public $dispatcher; + + public function foo(Event $e, $name, $dispatcher) + { + $this->name = $name; + $this->dispatcher = $dispatcher; + } +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); + } +} + +class TestEventSubscriberWithPriorities implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'pre.foo' => array('preFoo', 10), + 'post.foo' => array('postFoo'), + ); + } +} + +class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('pre.foo' => array( + array('preFoo1'), + array('preFoo2', 10) + )); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php new file mode 100644 index 000000000..7a20fe6bf --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Test class for Event. + */ +class EventTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Symfony\Component\EventDispatcher\Event + */ + protected $event; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcher + */ + protected $dispatcher; + + /** + * Sets up the fixture, for example, opens a network connection. + * This method is called before a test is executed. + */ + protected function setUp() + { + $this->event = new Event(); + $this->dispatcher = new EventDispatcher(); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() + { + $this->event = null; + $this->dispatcher = null; + } + + public function testIsPropagationStopped() + { + $this->assertFalse($this->event->isPropagationStopped()); + } + + public function testStopPropagationAndIsPropagationStopped() + { + $this->event->stopPropagation(); + $this->assertTrue($this->event->isPropagationStopped()); + } + + public function testSetDispatcher() + { + $this->event->setDispatcher($this->dispatcher); + $this->assertSame($this->dispatcher, $this->event->getDispatcher()); + } + + public function testGetDispatcher() + { + $this->assertNull($this->event->getDispatcher()); + } + + public function testGetName() + { + $this->assertNull($this->event->getName()); + } + + public function testSetName() + { + $this->event->setName('foo'); + $this->assertEquals('foo', $this->event->getName()); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php new file mode 100644 index 000000000..5dfda92f2 --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php @@ -0,0 +1,140 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\GenericEvent; + +/** + * Test class for Event. + */ +class GenericEventTest extends \PHPUnit_Framework_TestCase +{ + + /** + * @var GenericEvent + */ + private $event; + + private $subject; + + /** + * Prepares the environment before running a test. + */ + protected function setUp() + { + parent::setUp(); + + $this->subject = new \stdClass(); + $this->event = new GenericEvent($this->subject, array('name' => 'Event')); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->subject = null; + $this->event = null; + + parent::tearDown(); + } + + public function testConstruct() + { + $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); + } + + /** + * Tests Event->getArgs() + */ + public function testGetArguments() + { + // test getting all + $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); + } + + public function testSetArguments() + { + $result = $this->event->setArguments(array('foo' => 'bar')); + $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); + $this->assertSame($this->event, $result); + } + + public function testSetArgument() + { + $result = $this->event->setArgument('foo2', 'bar2'); + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + $this->assertEquals($this->event, $result); + } + + public function testGetArgument() + { + // test getting key + $this->assertEquals('Event', $this->event->getArgument('name')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetArgException() + { + $this->event->getArgument('nameNotExist'); + } + + public function testOffsetGet() + { + // test getting key + $this->assertEquals('Event', $this->event['name']); + + // test getting invalid arg + $this->setExpectedException('InvalidArgumentException'); + $this->assertFalse($this->event['nameNotExist']); + } + + public function testOffsetSet() + { + $this->event['foo2'] = 'bar2'; + $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); + } + + public function testOffsetUnset() + { + unset($this->event['name']); + $this->assertAttributeSame(array(), 'arguments', $this->event); + } + + public function testOffsetIsset() + { + $this->assertTrue(isset($this->event['name'])); + $this->assertFalse(isset($this->event['nameNotExist'])); + } + + public function testHasArgument() + { + $this->assertTrue($this->event->hasArgument('name')); + $this->assertFalse($this->event->hasArgument('nameNotExist')); + } + + public function testGetSubject() + { + $this->assertSame($this->subject, $this->event->getSubject()); + } + + public function testHasIterator() + { + $data = array(); + foreach ($this->event as $key => $value) { + $data[$key] = $value; + } + $this->assertEquals(array('name' => 'Event'), $data); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 000000000..80a7e43be --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\EventDispatcher\Tests; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; + +/** + * @author Bernhard Schussek + */ +class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $innerDispatcher; + + /** + * @var ImmutableEventDispatcher + */ + private $dispatcher; + + protected function setUp() + { + $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); + $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); + } + + public function testDispatchDelegates() + { + $event = new Event(); + + $this->innerDispatcher->expects($this->once()) + ->method('dispatch') + ->with('event', $event) + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); + } + + public function testGetListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('getListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->getListeners('event')); + } + + public function testHasListenersDelegates() + { + $this->innerDispatcher->expects($this->once()) + ->method('hasListeners') + ->with('event') + ->will($this->returnValue('result')); + + $this->assertSame('result', $this->dispatcher->hasListeners('event')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddListenerDisallowed() + { + $this->dispatcher->addListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testAddSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->addSubscriber($subscriber); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveListenerDisallowed() + { + $this->dispatcher->removeListener('event', function () { return 'foo'; }); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testRemoveSubscriberDisallowed() + { + $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $this->dispatcher->removeSubscriber($subscriber); + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json new file mode 100644 index 000000000..e748c506c --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json @@ -0,0 +1,38 @@ +{ + "name": "symfony/event-dispatcher", + "type": "library", + "description": "Symfony EventDispatcher Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + }, + "target-dir": "Symfony/Component/EventDispatcher", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist new file mode 100644 index 000000000..0c3de4f7b --- /dev/null +++ b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md new file mode 100644 index 000000000..5b5cd6a6c --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +2.3.12 +------ + + * deprecated dumpFile() file mode argument. + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php new file mode 100644 index 000000000..c212e664d --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + * + * @api + */ +interface ExceptionInterface +{ +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php new file mode 100644 index 000000000..15533db40 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a file couldn't be found + * + * @author Fabien Potencier + * @author Christian Gärtner + */ +class FileNotFoundException extends IOException +{ + public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) + { + if (null === $message) { + if (null === $path) { + $message = 'File could not be found.'; + } else { + $message = sprintf('File "%s" could not be found.', $path); + } + } + + parent::__construct($message, $code, $previous, $path); + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php new file mode 100644 index 000000000..4b551af71 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens + * + * @author Romain Neutron + * @author Christian Gärtner + * @author Fabien Potencier + * + * @api + */ +class IOException extends \RuntimeException implements IOExceptionInterface +{ + private $path; + + public function __construct($message, $code = 0, \Exception $previous = null, $path = null) + { + $this->path = $path; + + parent::__construct($message, $code, $previous); + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->path; + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php new file mode 100644 index 000000000..c88c76317 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * IOException interface for file and input/output stream related exceptions thrown by the component. + * + * @author Christian Gärtner + */ +interface IOExceptionInterface extends ExceptionInterface +{ + /** + * Returns the associated path for the exception + * + * @return string The path. + */ + public function getPath(); +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php new file mode 100644 index 000000000..ca146aa81 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php @@ -0,0 +1,477 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Exception\FileNotFoundException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + /** + * Copies a file. + * + * This method only copies the file if the origin file is newer than the target file. + * + * By default, if the target already exists, it is not overridden. + * + * @param string $originFile The original filename + * @param string $targetFile The target filename + * @param bool $override Whether to override an existing file or not + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy($originFile, $targetFile, $override = false) + { + if (stream_is_local($originFile) && !is_file($originFile)) { + throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + } + + $this->mkdir(dirname($targetFile)); + + if (!$override && is_file($targetFile) && null === parse_url($originFile, PHP_URL_HOST)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } else { + $doCopy = true; + } + + if ($doCopy) { + // https://bugs.php.net/bug.php?id=64634 + $source = fopen($originFile, 'r'); + $target = fopen($targetFile, 'w'); + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + } + } + } + + /** + * Creates a directory recursively. + * + * @param string|array|\Traversable $dirs The directory path + * @param int $mode The directory mode + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, $mode = 0777) + { + foreach ($this->toIterator($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (true !== @mkdir($dir, $mode, true)) { + throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir); + } + } + } + + /** + * Checks the existence of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check + * + * @return bool true if the file exists, false otherwise + */ + public function exists($files) + { + foreach ($this->toIterator($files) as $file) { + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create + * @param int $time The touch time as a Unix timestamp + * @param int $atime The access time as a Unix timestamp + * + * @throws IOException When touch fails + */ + public function touch($files, $time = null, $atime = null) + { + foreach ($this->toIterator($files) as $file) { + $touch = $time ? @touch($file, $time, $atime) : @touch($file); + if (true !== $touch) { + throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file); + } + } + } + + /** + * Removes files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + $files = iterator_to_array($this->toIterator($files)); + $files = array_reverse($files); + foreach ($files as $file) { + if (!file_exists($file) && !is_link($file)) { + continue; + } + + if (is_dir($file) && !is_link($file)) { + $this->remove(new \FilesystemIterator($file)); + + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove directory "%s".', $file), 0, null, $file); + } + } else { + // https://bugs.php.net/bug.php?id=52176 + if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) { + if (true !== @rmdir($file)) { + throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file); + } + } else { + if (true !== @unlink($file)) { + throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file); + } + } + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fail + */ + public function chmod($files, $mode, $umask = 0000, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + if (true !== @chmod($file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file); + } + } + } + + /** + * Change the owner of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string $user The new owner user name + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fail + */ + public function chown($files, $user, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && function_exists('lchown')) { + if (true !== @lchown($file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } else { + if (true !== @chown($file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } + } + } + + /** + * Change the group of an array of files or directories + * + * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group + * @param string $group The group name + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fail + */ + public function chgrp($files, $group, $recursive = false) + { + foreach ($this->toIterator($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && function_exists('lchgrp')) { + if (true !== @lchgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } else { + if (true !== @chgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @param string $origin The origin filename or directory + * @param string $target The new filename or directory + * @param bool $overwrite Whether to overwrite the target if it already exists + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename($origin, $target, $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && is_readable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + } + + if (true !== @rename($origin, $target)) { + throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); + } + } + + /** + * Creates a symbolic link or copy a directory. + * + * @param string $originDir The origin directory path + * @param string $targetDir The symbolic link name + * @param bool $copyOnWindows Whether to copy files if on Windows + * + * @throws IOException When symlink fails + */ + public function symlink($originDir, $targetDir, $copyOnWindows = false) + { + if (!function_exists('symlink') && $copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + + $this->mkdir(dirname($targetDir)); + + $ok = false; + if (is_link($targetDir)) { + if (readlink($targetDir) != $originDir) { + $this->remove($targetDir); + } else { + $ok = true; + } + } + + if (!$ok) { + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (is_array($report)) { + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) { + throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?'); + } + } + + throw new IOException(sprintf('Failed to create symbolic link from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir); + } + } + } + + /** + * Given an existing path, convert it to a path relative to a given starting path + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + // Normalize separators on Windows + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $endPath = strtr($endPath, '\\', '/'); + $startPath = strtr($startPath, '\\', '/'); + } + + // Split the paths into arrays + $startPathArr = explode('/', trim($startPath, '/')); + $endPathArr = explode('/', trim($endPath, '/')); + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + $index++; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + $depth = count($startPathArr) - $index; + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : ''); + + return (strlen($relativePath) === 0) ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * @param string $originDir The origin directory + * @param string $targetDir The target directory + * @param \Traversable $iterator A Traversable instance + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] Whether to override an existing file on copy or not (see copy()) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + foreach ($deleteIterator as $file) { + $origin = str_replace($targetDir, $originDir, $file->getPathname()); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = false; + if (isset($options['copy_on_windows']) && !function_exists('symlink')) { + $copyOnWindows = $options['copy_on_windows']; + } + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + foreach ($iterator as $file) { + $target = str_replace($originDir, $targetDir, $file->getPathname()); + + if ($copyOnWindows) { + if (is_link($file) || is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } elseif (is_dir($file)) { + $this->mkdir($target); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } else { + if (is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } + } + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + public function isAbsolutePath($file) + { + if (strspn($file, '/\\', 0, 1) + || (strlen($file) > 3 && ctype_alpha($file[0]) + && substr($file, 1, 1) === ':' + && (strspn($file, '/\\', 2, 1)) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } + + /** + * Atomically dumps content into a file. + * + * @param string $filename The file to be written to. + * @param string $content The data to write into the file. + * @param null|int $mode The file mode (octal). If null, file permissions are not modified + * Deprecated since version 2.3.12, to be removed in 3.0. + * @throws IOException If the file cannot be written to. + */ + public function dumpFile($filename, $content, $mode = 0666) + { + $dir = dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } elseif (!is_writable($dir)) { + throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); + } + + $tmpFile = tempnam($dir, basename($filename)); + + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } + + $this->rename($tmpFile, $filename, true); + if (null !== $mode) { + $this->chmod($filename, $mode); + } + } + + /** + * @param mixed $files + * + * @return \Traversable + */ + private function toIterator($files) + { + if (!$files instanceof \Traversable) { + $files = new \ArrayObject(is_array($files) ? $files : array($files)); + } + + return $files; + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md new file mode 100644 index 000000000..7d8a4749e --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md @@ -0,0 +1,45 @@ +Filesystem Component +==================== + +Filesystem provides basic utility to manipulate the file system: + +```php +copy($originFile, $targetFile, $override = false); + +$filesystem->mkdir($dirs, $mode = 0777); + +$filesystem->touch($files, $time = null, $atime = null); + +$filesystem->remove($files); + +$filesystem->chmod($files, $mode, $umask = 0000, $recursive = false); + +$filesystem->chown($files, $user, $recursive = false); + +$filesystem->chgrp($files, $group, $recursive = false); + +$filesystem->rename($origin, $target); + +$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false); + +$filesystem->makePathRelative($endPath, $startPath); + +$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()); + +$filesystem->isAbsolutePath($file); +``` + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Filesystem/ + $ composer.phar install + $ phpunit diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php new file mode 100644 index 000000000..53bd8db76 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Exception\FileNotFoundException; + +/** + * Test class for Filesystem. + */ +class ExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testGetPath() + { + $e = new IOException('', 0, null, '/foo'); + $this->assertEquals('/foo', $e->getPath(), 'The pass should be returned.'); + } + + public function testGeneratedMessage() + { + $e = new FileNotFoundException(null, 0, null, '/foo'); + $this->assertEquals('/foo', $e->getPath()); + $this->assertEquals('File "/foo" could not be found.', $e->getMessage(), 'A message should be generated.'); + } + + public function testGeneratedMessageWithoutPath() + { + $e = new FileNotFoundException(); + $this->assertEquals('File could not be found.', $e->getMessage(), 'A message should be generated.'); + } + + public function testCustomMessage() + { + $e = new FileNotFoundException('bar', 0, null, '/foo'); + $this->assertEquals('bar', $e->getMessage(), 'A custom message should be possible still.'); + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php new file mode 100644 index 000000000..aad5b9991 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -0,0 +1,907 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +use Symfony\Component\Filesystem\Filesystem; + +/** + * Test class for Filesystem. + */ +class FilesystemTest extends FilesystemTestCase +{ + /** + * @var \Symfony\Component\Filesystem\Filesystem $filesystem + */ + private $filesystem = null; + + public function setUp() + { + parent::setUp(); + $this->filesystem = new Filesystem(); + } + + public function testCopyCreatesNewFile() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyFails() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + } + + public function testCopyOverridesExistingFileIfModified() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + touch($targetFilePath, time() - 1000); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyDoesNotOverrideExistingFileByDefault() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); + } + + public function testCopyOverridesExistingFileIfForced() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, true); + + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyCreatesTargetDirectoryIfItDoesNotExist() + { + $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertTrue(is_dir($targetFileDirectory)); + $this->assertFileExists($targetFilePath); + $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); + } + + public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToNotCopy() + { + $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png'; + $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($targetFilePath, 'TARGET FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, false); + + $this->assertFileExists($targetFilePath); + $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath)); + } + + public function testMkdirCreatesDirectoriesRecursively() + { + $directory = $this->workspace + .DIRECTORY_SEPARATOR.'directory' + .DIRECTORY_SEPARATOR.'sub_directory'; + + $this->filesystem->mkdir($directory); + + $this->assertTrue(is_dir($directory)); + } + + public function testMkdirCreatesDirectoriesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + public function testMkdirCreatesDirectoriesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $directories = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testMkdirCreatesDirectoriesFails() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $dir = $basePath.'2'; + + file_put_contents($dir, ''); + + $this->filesystem->mkdir($dir); + } + + public function testTouchCreatesEmptyFile() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; + + $this->filesystem->touch($file); + + $this->assertFileExists($file); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTouchFails() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; + + $this->filesystem->touch($file); + } + + public function testTouchCreatesEmptyFilesFromArray() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = array( + $basePath.'1', $basePath.'2', $basePath.'3' + ); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testTouchCreatesEmptyFilesFromTraversableObject() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + $files = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3' + )); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testRemoveCleansFilesAndDirectoriesIteratively() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testRemoveCleansArrayOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveCleansTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + $this->assertTrue(!is_file($basePath.'file')); + } + + public function testRemoveIgnoresNonExistingFiles() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + + $files = array( + $basePath.'dir', $basePath.'file' + ); + + $this->filesystem->remove($files); + + $this->assertTrue(!is_dir($basePath.'dir')); + } + + public function testRemoveCleansInvalidLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + // create symlink to nonexistent file + @symlink($basePath.'file', $basePath.'link'); + + $this->filesystem->remove($basePath); + + $this->assertTrue(!is_dir($basePath)); + } + + public function testFilesExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + mkdir($basePath); + touch($basePath.'file1'); + mkdir($basePath.'folder'); + + $this->assertTrue($this->filesystem->exists($basePath.'file1')); + $this->assertTrue($this->filesystem->exists($basePath.'folder')); + } + + public function testFilesExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file' + )); + + $this->assertTrue($this->filesystem->exists($files)); + } + + public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + touch($basePath.'file2'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', $basePath.'file2' + )); + + unlink($basePath.'file'); + + $this->assertFalse($this->filesystem->exists($files)); + } + + public function testInvalidFileNotExists() + { + $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; + + $this->assertFalse($this->filesystem->exists($basePath.time())); + } + + public function testChmodChangesFileMode() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400); + $this->filesystem->chmod($dir, 0753); + + $this->assertFilePermissions(753, $dir); + $this->assertFilePermissions(400, $file); + } + + public function testChmodWrongMod() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($dir); + + $this->filesystem->chmod($dir, 'Wrongmode'); + } + + public function testChmodRecursive() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400, 0000, true); + $this->filesystem->chmod($dir, 0753, 0000, true); + + $this->assertFilePermissions(753, $dir); + $this->assertFilePermissions(753, $file); + } + + public function testChmodAppliesUmask() + { + $this->markAsSkippedIfChmodIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0770, 0022); + $this->assertFilePermissions(750, $file); + } + + public function testChmodChangesModeOfArrayOfFiles() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = array($directory, $file); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertFilePermissions(753, $file); + $this->assertFilePermissions(753, $directory); + } + + public function testChmodChangesModeOfTraversableFileObject() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $files = new \ArrayObject(array($directory, $file)); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertFilePermissions(753, $file); + $this->assertFilePermissions(753, $directory); + } + + public function testChown() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, $this->getFileOwner($dir)); + } + + public function testChownRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chown($dir, $this->getFileOwner($dir), true); + } + + public function testChownSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, $this->getFileOwner($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testChgrp() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); + } + + public function testChgrpRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); + } + + public function testChgrpSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, $this->getFileGroup($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testRename() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + touch($file); + + $this->filesystem->rename($file, $newPath); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionIfTargetAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath); + } + + public function testRenameOverwritesTheTargetIfItAlreadyExists() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath, true); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionOnError() + { + $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid(); + $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; + + $this->filesystem->rename($file, $newPath); + } + + public function testSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + /** + * @depends testSymlink + */ + public function testRemoveSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + $this->filesystem->remove($link); + + $this->assertTrue(!is_link($link)); + } + + public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($this->workspace, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkIsNotOverwrittenIfAlreadyCreated() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($file, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; + $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; + $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link1); + $this->filesystem->symlink($file, $link2); + + $this->assertTrue(is_link($link1)); + $this->assertEquals($file, readlink($link1)); + $this->assertTrue(is_link($link2)); + $this->assertEquals($file, readlink($link2)); + } + + /** + * @dataProvider providePathsForMakePathRelative + */ + public function testMakePathRelative($endPath, $startPath, $expectedPath) + { + $path = $this->filesystem->makePathRelative($endPath, $startPath); + + $this->assertEquals($expectedPath, $path); + } + + /** + * @return array + */ + public function providePathsForMakePathRelative() + { + $paths = array( + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), + array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), + array('/aa/bb', '/aa/bb', './'), + array('/aa/bb', '/aa/bb/', './'), + array('/aa/bb/', '/aa/bb', './'), + array('/aa/bb/', '/aa/bb/', './'), + array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc', '/aa', 'bb/cc/'), + array('/aa/bb/cc', '/aa/', 'bb/cc/'), + array('/aa/bb/cc/', '/aa', 'bb/cc/'), + array('/aa/bb/cc/', '/aa/', 'bb/cc/'), + array('/a/aab/bb', '/a/aa', '../aab/bb/'), + array('/a/aab/bb', '/a/aa/', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), + ); + + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); + } + + return $paths; + } + + public function testMirrorCopiesFilesAndDirectoriesRecursively() + { + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; + $file1 = $directory.'file1'; + $file2 = $sourcePath.'file2'; + + mkdir($sourcePath); + mkdir($directory); + file_put_contents($file1, 'FILE1'); + file_put_contents($file2, 'FILE2'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertTrue(is_dir($targetPath.'directory')); + $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); + $this->assertFileEquals($file2, $targetPath.'file2'); + + $this->filesystem->remove($file1); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + file_put_contents($file1, 'FILE1'); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->remove($directory); + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory')); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); + } + + public function testMirrorCopiesLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + file_put_contents($sourcePath.'file1', 'FILE1'); + symlink($sourcePath.'file1', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'file1', $targetPath.DIRECTORY_SEPARATOR.'link1'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorCopiesLinkedDirectoryContents() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; + + mkdir($sourcePath.'nested/', 0777, true); + file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); + // Note: We symlink directory, not file + symlink($sourcePath.'nested', $sourcePath.'link1'); + + $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt'); + $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); + } + + /** + * @dataProvider providePathsForIsAbsolutePath + */ + public function testIsAbsolutePath($path, $expectedResult) + { + $result = $this->filesystem->isAbsolutePath($path); + + $this->assertEquals($expectedResult, $result); + } + + /** + * @return array + */ + public function providePathsForIsAbsolutePath() + { + return array( + array('/var/lib', true), + array('c:\\\\var\\lib', true), + array('\\var\\lib', true), + array('var/lib', false), + array('../var/lib', false), + array('', false), + array(null, false) + ); + } + + public function testDumpFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar', 0753); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + + // skip mode check on Windows + if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->assertFilePermissions(753, $filename); + } + } + + public function testDumpFileWithNullMode() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar', null); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + + // skip mode check on Windows + if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->assertFilePermissions(600, $filename); + } + } + + public function testDumpFileOverwritesAnExistingFile() + { + $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + + $this->filesystem->dumpFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertSame('bar', file_get_contents($filename)); + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php new file mode 100644 index 000000000..b4b223018 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +class FilesystemTestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @var string $workspace + */ + protected $workspace = null; + + protected static $symlinkOnWindows = null; + + public static function setUpBeforeClass() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + static::$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)')) { + static::$symlinkOnWindows = false; + } + } + } + } + + public function setUp() + { + $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + public function tearDown() + { + $this->clean($this->workspace); + } + + /** + * @param string $file + */ + protected 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); + } + } + + /** + * @param int $expectedFilePerms expected file permissions as three digits (i.e. 755) + * @param string $filePath + */ + protected function assertFilePermissions($expectedFilePerms, $filePath) + { + $actualFilePerms = (int) substr(sprintf('%o', fileperms($filePath)), -3); + $this->assertEquals( + $expectedFilePerms, + $actualFilePerms, + sprintf('File permissions for %s must be %s. Actual %s', $filePath, $expectedFilePerms, $actualFilePerms) + ); + } + + protected function getFileOwner($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getpwuid($infos['uid'])) { + return $datas['name']; + } + } + + protected function getFileGroup($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getgrgid($infos['gid'])) { + return $datas['name']; + } + } + + protected function markAsSkippedIfSymlinkIsMissing() + { + if (!function_exists('symlink')) { + $this->markTestSkipped('symlink is not supported'); + } + + if (defined('PHP_WINDOWS_VERSION_MAJOR') && false === static::$symlinkOnWindows) { + $this->markTestSkipped('symlink requires "Create symbolic links" privilege on windows'); + } + } + + protected function markAsSkippedIfChmodIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $this->markTestSkipped('chmod is not supported on windows'); + } + } + + protected function markAsSkippedIfPosixIsMissing() + { + if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) { + $this->markTestSkipped('Posix is not supported'); + } + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json new file mode 100644 index 000000000..bab7c4fb9 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Symfony Filesystem Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Filesystem\\": "" } + }, + "target-dir": "Symfony/Component/Filesystem", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist new file mode 100644 index 000000000..ef0bf9541 --- /dev/null +++ b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + + + + diff --git a/core/lib/symfony/process/Symfony/Component/Process/.gitignore b/core/lib/symfony/process/Symfony/Component/Process/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md b/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md new file mode 100644 index 000000000..3d13b99cc --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md @@ -0,0 +1,31 @@ +CHANGELOG +========= + +2.4.0 +----- + + * added the ability to define an idle timeout + +2.3.0 +----- + + * added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows + * added Process::signal() + * added Process::getPid() + * added support for a TTY mode + +2.2.0 +----- + + * added ProcessBuilder::setArguments() to reset the arguments on a builder + * added a way to retrieve the standard and error output incrementally + * added Process:restart() + +2.1.0 +----- + + * added support for non-blocking processes (start(), wait(), isRunning(), stop()) + * enhanced Windows compatibility + * added Process::getExitCodeText() that returns a string representation for + the exit code returned by the process + * added ProcessBuilder diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php new file mode 100644 index 000000000..75c1c9e5d --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * Marker Interface for the Process Component. + * + * @author Johannes M. Schmitt + */ +interface ExceptionInterface +{ +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php new file mode 100644 index 000000000..926ee2118 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * InvalidArgumentException for the Process Component. + * + * @author Romain Neutron + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php new file mode 100644 index 000000000..be3d490dd --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * LogicException for the Process Component. + * + * @author Romain Neutron + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php new file mode 100644 index 000000000..890935933 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception for failed processes. + * + * @author Johannes M. Schmitt + */ +class ProcessFailedException extends RuntimeException +{ + private $process; + + public function __construct(Process $process) + { + if ($process->isSuccessful()) { + throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); + } + + parent::__construct( + sprintf( + 'The command "%s" failed.'."\nExit Code: %s(%s)\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", + $process->getCommandLine(), + $process->getExitCode(), + $process->getExitCodeText(), + $process->getOutput(), + $process->getErrorOutput() + ) + ); + + $this->process = $process; + } + + public function getProcess() + { + return $this->process; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php new file mode 100644 index 000000000..d45114696 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +use Symfony\Component\Process\Process; + +/** + * Exception that is thrown when a process times out. + * + * @author Johannes M. Schmitt + */ +class ProcessTimedOutException extends RuntimeException +{ + const TYPE_GENERAL = 1; + const TYPE_IDLE = 2; + + private $process; + private $timeoutType; + + public function __construct(Process $process, $timeoutType) + { + $this->process = $process; + $this->timeoutType = $timeoutType; + + parent::__construct(sprintf( + 'The process "%s" exceeded the timeout of %s seconds.', + $process->getCommandLine(), + $this->getExceededTimeout() + )); + } + + public function getProcess() + { + return $this->process; + } + + public function isGeneralTimeout() + { + return $this->timeoutType === self::TYPE_GENERAL; + } + + public function isIdleTimeout() + { + return $this->timeoutType === self::TYPE_IDLE; + } + + public function getExceededTimeout() + { + switch ($this->timeoutType) { + case self::TYPE_GENERAL: + return $this->process->getTimeout(); + + case self::TYPE_IDLE: + return $this->process->getIdleTimeout(); + + default: + throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php new file mode 100644 index 000000000..adead2536 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Exception; + +/** + * RuntimeException for the Process Component. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php b/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php new file mode 100644 index 000000000..5cc99c769 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +/** + * Generic executable finder. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class ExecutableFinder +{ + private $suffixes = array('.exe', '.bat', '.cmd', '.com'); + + /** + * Replaces default suffixes of executable. + * + * @param array $suffixes + */ + public function setSuffixes(array $suffixes) + { + $this->suffixes = $suffixes; + } + + /** + * Adds new possible suffix to check for executable. + * + * @param string $suffix + */ + public function addSuffix($suffix) + { + $this->suffixes[] = $suffix; + } + + /** + * Finds an executable by name. + * + * @param string $name The executable name (without the extension) + * @param string $default The default to return if no executable is found + * @param array $extraDirs Additional dirs to check into + * + * @return string The executable path or default value + */ + public function find($name, $default = null, array $extraDirs = array()) + { + if (ini_get('open_basedir')) { + $searchPath = explode(PATH_SEPARATOR, getenv('open_basedir')); + $dirs = array(); + foreach ($searchPath as $path) { + if (is_dir($path)) { + $dirs[] = $path; + } else { + $file = str_replace(dirname($path), '', $path); + if ($file == $name && is_executable($path)) { + return $path; + } + } + } + } else { + $dirs = array_merge( + explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), + $extraDirs + ); + } + + $suffixes = array(''); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $pathExt = getenv('PATHEXT'); + $suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes; + } + foreach ($suffixes as $suffix) { + foreach ($dirs as $dir) { + if (is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && (defined('PHP_WINDOWS_VERSION_BUILD') || is_executable($file))) { + return $file; + } + } + } + + return $default; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/LICENSE b/core/lib/symfony/process/Symfony/Component/Process/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php b/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php new file mode 100644 index 000000000..9001f4109 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +/** + * An executable finder specifically designed for the PHP executable. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class PhpExecutableFinder +{ + private $executableFinder; + + public function __construct() + { + $this->executableFinder = new ExecutableFinder(); + } + + /** + * Finds The PHP executable. + * + * @return string|false The PHP executable path or false if it cannot be found + */ + public function find() + { + // HHVM support + if (defined('HHVM_VERSION') && false !== $hhvm = getenv('PHP_BINARY')) { + return $hhvm; + } + + // PHP_BINARY return the current sapi executable + if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server')) && is_file(PHP_BINARY)) { + return PHP_BINARY; + } + + if ($php = getenv('PHP_PATH')) { + if (!is_executable($php)) { + return false; + } + + return $php; + } + + if ($php = getenv('PHP_PEAR_PHP_BIN')) { + if (is_executable($php)) { + return $php; + } + } + + $dirs = array(PHP_BINDIR); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $dirs[] = 'C:\xampp\php\\'; + } + + return $this->executableFinder->find('php', false, $dirs); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php b/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php new file mode 100644 index 000000000..93948e1db --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * PhpProcess runs a PHP script in an independent process. + * + * $p = new PhpProcess(''); + * $p->run(); + * print $p->getOutput()."\n"; + * + * @author Fabien Potencier + * + * @api + */ +class PhpProcess extends Process +{ + private $executableFinder; + + /** + * Constructor. + * + * @param string $script The PHP script to run (as a string) + * @param string $cwd The working directory + * @param array $env The environment variables + * @param int $timeout The timeout in seconds + * @param array $options An array of options for proc_open + * + * @api + */ + public function __construct($script, $cwd = null, array $env = array(), $timeout = 60, array $options = array()) + { + parent::__construct(null, $cwd, $env, $script, $timeout, $options); + + $this->executableFinder = new PhpExecutableFinder(); + } + + /** + * Sets the path to the PHP binary to use. + * + * @api + */ + public function setPhpBinary($php) + { + $this->setCommandLine($php); + } + + /** + * {@inheritdoc} + */ + public function start($callback = null) + { + if (null === $this->getCommandLine()) { + if (false === $php = $this->executableFinder->find()) { + throw new RuntimeException('Unable to find the PHP executable.'); + } + $this->setCommandLine($php); + } + + parent::start($callback); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Process.php b/core/lib/symfony/process/Symfony/Component/Process/Process.php new file mode 100644 index 000000000..c49d7a56a --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Process.php @@ -0,0 +1,1312 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\InvalidArgumentException; +use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Exception\ProcessTimedOutException; +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * Process is a thin wrapper around proc_* functions to easily + * start independent PHP processes. + * + * @author Fabien Potencier + * + * @api + */ +class Process +{ + const ERR = 'err'; + const OUT = 'out'; + + const STATUS_READY = 'ready'; + const STATUS_STARTED = 'started'; + const STATUS_TERMINATED = 'terminated'; + + const STDIN = 0; + const STDOUT = 1; + const STDERR = 2; + + // Timeout Precision in seconds. + const TIMEOUT_PRECISION = 0.2; + + private $callback; + private $commandline; + private $cwd; + private $env; + private $stdin; + private $starttime; + private $lastOutputTime; + private $timeout; + private $idleTimeout; + private $options; + private $exitcode; + private $fallbackExitcode; + private $processInformation; + private $stdout; + private $stderr; + private $enhanceWindowsCompatibility = true; + private $enhanceSigchildCompatibility; + private $process; + private $status = self::STATUS_READY; + private $incrementalOutputOffset = 0; + private $incrementalErrorOutputOffset = 0; + private $tty; + + private $useFileHandles = false; + /** @var ProcessPipes */ + private $processPipes; + + private static $sigchild; + + /** + * Exit codes translation table. + * + * User-defined errors must use exit codes in the 64-113 range. + * + * @var array + */ + public static $exitCodes = array( + 0 => 'OK', + 1 => 'General error', + 2 => 'Misuse of shell builtins', + + 126 => 'Invoked command cannot execute', + 127 => 'Command not found', + 128 => 'Invalid exit argument', + + // signals + 129 => 'Hangup', + 130 => 'Interrupt', + 131 => 'Quit and dump core', + 132 => 'Illegal instruction', + 133 => 'Trace/breakpoint trap', + 134 => 'Process aborted', + 135 => 'Bus error: "access to undefined portion of memory object"', + 136 => 'Floating point exception: "erroneous arithmetic operation"', + 137 => 'Kill (terminate immediately)', + 138 => 'User-defined 1', + 139 => 'Segmentation violation', + 140 => 'User-defined 2', + 141 => 'Write to pipe with no one reading', + 142 => 'Signal raised by alarm', + 143 => 'Termination (request to terminate)', + // 144 - not defined + 145 => 'Child process terminated, stopped (or continued*)', + 146 => 'Continue if stopped', + 147 => 'Stop executing temporarily', + 148 => 'Terminal stop signal', + 149 => 'Background process attempting to read from tty ("in")', + 150 => 'Background process attempting to write to tty ("out")', + 151 => 'Urgent data available on socket', + 152 => 'CPU time limit exceeded', + 153 => 'File size limit exceeded', + 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"', + 155 => 'Profiling timer expired', + // 156 - not defined + 157 => 'Pollable event', + // 158 - not defined + 159 => 'Bad syscall', + ); + + /** + * Constructor. + * + * @param string $commandline The command line to run + * @param string|null $cwd The working directory or null to use the working dir of the current PHP process + * @param array|null $env The environment variables or null to inherit + * @param string|null $stdin The STDIN content + * @param int|float|null $timeout The timeout in seconds or null to disable + * @param array $options An array of options for proc_open + * + * @throws RuntimeException When proc_open is not installed + * + * @api + */ + public function __construct($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) + { + if (!function_exists('proc_open')) { + throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.'); + } + + $this->commandline = $commandline; + $this->cwd = $cwd; + + // on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started + // on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected + // @see : https://bugs.php.net/bug.php?id=51800 + // @see : https://bugs.php.net/bug.php?id=50524 + if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || defined('PHP_WINDOWS_VERSION_BUILD'))) { + $this->cwd = getcwd(); + } + if (null !== $env) { + $this->setEnv($env); + } + + $this->stdin = $stdin; + $this->setTimeout($timeout); + $this->useFileHandles = defined('PHP_WINDOWS_VERSION_BUILD'); + $this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled(); + $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options); + } + + public function __destruct() + { + // stop() will check if we have a process running. + $this->stop(); + } + + public function __clone() + { + $this->resetProcessData(); + } + + /** + * Runs the process. + * + * The callback receives the type of output (out or err) and + * some bytes from the output in real-time. It allows to have feedback + * from the independent process during execution. + * + * The STDOUT and STDERR are also available after the process is finished + * via the getOutput() and getErrorOutput() methods. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return int The exit status code + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process stopped after receiving signal + * + * @api + */ + public function run($callback = null) + { + $this->start($callback); + + return $this->wait(); + } + + /** + * Starts the process and returns after sending the STDIN. + * + * This method blocks until all STDIN data is sent to the process then it + * returns while the process runs in the background. + * + * The termination of the process can be awaited with wait(). + * + * The callback receives the type of output (out or err) and some bytes from + * the output in real-time while writing the standard input to the process. + * It allows to have feedback from the independent process during execution. + * If there is no callback passed, the wait() method can be called + * with true as a second parameter then the callback will get all data occurred + * in (and since) the start call. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The process itself + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process is already running + */ + public function start($callback = null) + { + if ($this->isRunning()) { + throw new RuntimeException('Process is already running'); + } + + $this->resetProcessData(); + $this->starttime = $this->lastOutputTime = microtime(true); + $this->callback = $this->buildCallback($callback); + $descriptors = $this->getDescriptors(); + + $commandline = $this->commandline; + + if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) { + $commandline = 'cmd /V:ON /E:ON /C "('.$commandline.')'; + foreach ($this->processPipes->getFiles() as $offset => $filename) { + $commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename); + } + $commandline .= '"'; + + if (!isset($this->options['bypass_shell'])) { + $this->options['bypass_shell'] = true; + } + } + + $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); + + if (!is_resource($this->process)) { + throw new RuntimeException('Unable to launch a new process.'); + } + $this->status = self::STATUS_STARTED; + + $this->processPipes->unblock(); + + if ($this->tty) { + return; + } + + $this->processPipes->write(false, $this->stdin); + $this->updateStatus(false); + $this->checkTimeout(); + } + + /** + * Restarts the process. + * + * Be warned that the process is cloned before being started. + * + * @param callable|null $callback A PHP callback to run whenever there is some + * output available on STDOUT or STDERR + * + * @return Process The new process + * + * @throws RuntimeException When process can't be launched + * @throws RuntimeException When process is already running + * + * @see start() + */ + public function restart($callback = null) + { + if ($this->isRunning()) { + throw new RuntimeException('Process is already running'); + } + + $process = clone $this; + $process->start($callback); + + return $process; + } + + /** + * Waits for the process to terminate. + * + * The callback receives the type of output (out or err) and some bytes + * from the output in real-time while writing the standard input to the process. + * It allows to have feedback from the independent process during execution. + * + * @param callable|null $callback A valid PHP callback + * + * @return int The exitcode of the process + * + * @throws RuntimeException When process timed out + * @throws RuntimeException When process stopped after receiving signal + * @throws LogicException When process is not yet started + */ + public function wait($callback = null) + { + $this->requireProcessIsStarted(__FUNCTION__); + + $this->updateStatus(false); + if (null !== $callback) { + $this->callback = $this->buildCallback($callback); + } + + do { + $this->checkTimeout(); + $running = defined('PHP_WINDOWS_VERSION_BUILD') ? $this->isRunning() : $this->processPipes->hasOpenHandles(); + $close = !defined('PHP_WINDOWS_VERSION_BUILD') || !$running;; + $this->readPipes(true, $close); + } while ($running); + + while ($this->isRunning()) { + usleep(1000); + } + + if ($this->processInformation['signaled']) { + throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); + } + + return $this->exitcode; + } + + /** + * Returns the Pid (process identifier), if applicable. + * + * @return int|null The process id if running, null otherwise + * + * @throws RuntimeException In case --enable-sigchild is activated + */ + public function getPid() + { + if ($this->isSigchildEnabled()) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->isRunning() ? $this->processInformation['pid'] : null; + } + + /** + * Sends a POSIX signal to the process. + * + * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php) + * + * @return Process + * + * @throws LogicException In case the process is not running + * @throws RuntimeException In case --enable-sigchild is activated + * @throws RuntimeException In case of failure + */ + public function signal($signal) + { + $this->doSignal($signal, true); + + return $this; + } + + /** + * Returns the current output of the process (STDOUT). + * + * @return string The process output + * + * @throws LogicException In case the process is not started + * + * @api + */ + public function getOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); + + return $this->stdout; + } + + /** + * Returns the output incrementally. + * + * In comparison with the getOutput method which always return the whole + * output, this one returns the new output since the last call. + * + * @throws LogicException In case the process is not started + * + * @return string The process output since the last call + */ + public function getIncrementalOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $data = $this->getOutput(); + + $latest = substr($data, $this->incrementalOutputOffset); + $this->incrementalOutputOffset = strlen($data); + + return $latest; + } + + /** + * Clears the process output. + * + * @return Process + */ + public function clearOutput() + { + $this->stdout = ''; + $this->incrementalOutputOffset = 0; + + return $this; + } + + /** + * Returns the current error output of the process (STDERR). + * + * @return string The process error output + * + * @throws LogicException In case the process is not started + * + * @api + */ + public function getErrorOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); + + return $this->stderr; + } + + /** + * Returns the errorOutput incrementally. + * + * In comparison with the getErrorOutput method which always return the + * whole error output, this one returns the new error output since the last + * call. + * + * @throws LogicException In case the process is not started + * + * @return string The process error output since the last call + */ + public function getIncrementalErrorOutput() + { + $this->requireProcessIsStarted(__FUNCTION__); + + $data = $this->getErrorOutput(); + + $latest = substr($data, $this->incrementalErrorOutputOffset); + $this->incrementalErrorOutputOffset = strlen($data); + + return $latest; + } + + /** + * Clears the process output. + * + * @return Process + */ + public function clearErrorOutput() + { + $this->stderr = ''; + $this->incrementalErrorOutputOffset = 0; + + return $this; + } + + /** + * Returns the exit code returned by the process. + * + * @return null|int The exit status code, null if the Process is not terminated + * + * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled + * + * @api + */ + public function getExitCode() + { + if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); + } + + $this->updateStatus(false); + + return $this->exitcode; + } + + /** + * Returns a string representation for the exit code returned by the process. + * + * This method relies on the Unix exit code status standardization + * and might not be relevant for other operating systems. + * + * @return null|string A string representation for the exit status code, null if the Process is not terminated. + * + * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled + * + * @see http://tldp.org/LDP/abs/html/exitcodes.html + * @see http://en.wikipedia.org/wiki/Unix_signal + */ + public function getExitCodeText() + { + if (null === $exitcode = $this->getExitCode()) { + return; + } + + return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error'; + } + + /** + * Checks if the process ended successfully. + * + * @return bool true if the process ended successfully, false otherwise + * + * @api + */ + public function isSuccessful() + { + return 0 === $this->getExitCode(); + } + + /** + * Returns true if the child process has been terminated by an uncaught signal. + * + * It always returns false on Windows. + * + * @return bool + * + * @throws RuntimeException In case --enable-sigchild is activated + * @throws LogicException In case the process is not terminated + * + * @api + */ + public function hasBeenSignaled() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + if ($this->isSigchildEnabled()) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->processInformation['signaled']; + } + + /** + * Returns the number of the signal that caused the child process to terminate its execution. + * + * It is only meaningful if hasBeenSignaled() returns true. + * + * @return int + * + * @throws RuntimeException In case --enable-sigchild is activated + * @throws LogicException In case the process is not terminated + * + * @api + */ + public function getTermSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + if ($this->isSigchildEnabled()) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); + } + + $this->updateStatus(false); + + return $this->processInformation['termsig']; + } + + /** + * Returns true if the child process has been stopped by a signal. + * + * It always returns false on Windows. + * + * @return bool + * + * @throws LogicException In case the process is not terminated + * + * @api + */ + public function hasBeenStopped() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + $this->updateStatus(false); + + return $this->processInformation['stopped']; + } + + /** + * Returns the number of the signal that caused the child process to stop its execution. + * + * It is only meaningful if hasBeenStopped() returns true. + * + * @return int + * + * @throws LogicException In case the process is not terminated + * + * @api + */ + public function getStopSignal() + { + $this->requireProcessIsTerminated(__FUNCTION__); + + $this->updateStatus(false); + + return $this->processInformation['stopsig']; + } + + /** + * Checks if the process is currently running. + * + * @return bool true if the process is currently running, false otherwise + */ + public function isRunning() + { + if (self::STATUS_STARTED !== $this->status) { + return false; + } + + $this->updateStatus(false); + + return $this->processInformation['running']; + } + + /** + * Checks if the process has been started with no regard to the current state. + * + * @return bool true if status is ready, false otherwise + */ + public function isStarted() + { + return $this->status != self::STATUS_READY; + } + + /** + * Checks if the process is terminated. + * + * @return bool true if process is terminated, false otherwise + */ + public function isTerminated() + { + $this->updateStatus(false); + + return $this->status == self::STATUS_TERMINATED; + } + + /** + * Gets the process status. + * + * The status is one of: ready, started, terminated. + * + * @return string The current process status + */ + public function getStatus() + { + $this->updateStatus(false); + + return $this->status; + } + + /** + * Stops the process. + * + * @param int|float $timeout The timeout in seconds + * @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL + * + * @return int The exit-code of the process + * + * @throws RuntimeException if the process got signaled + */ + public function stop($timeout = 10, $signal = null) + { + $timeoutMicro = microtime(true) + $timeout; + if ($this->isRunning()) { + if (defined('PHP_WINDOWS_VERSION_BUILD') && !$this->isSigchildEnabled()) { + exec(sprintf("taskkill /F /T /PID %d 2>&1", $this->getPid()), $output, $exitCode); + if ($exitCode > 0) { + throw new RuntimeException('Unable to kill the process'); + } + } + proc_terminate($this->process); + do { + usleep(1000); + } while ($this->isRunning() && microtime(true) < $timeoutMicro); + + if ($this->isRunning() && !$this->isSigchildEnabled()) { + if (null !== $signal || defined('SIGKILL')) { + // avoid exception here : + // process is supposed to be running, but it might have stop + // just after this line. + // in any case, let's silently discard the error, we can not do anything + $this->doSignal($signal ?: SIGKILL, false); + } + } + } + + $this->updateStatus(false); + if ($this->processInformation['running']) { + $this->close(); + } + + return $this->exitcode; + } + + /** + * Adds a line to the STDOUT stream. + * + * @param string $line The line to append + */ + public function addOutput($line) + { + $this->lastOutputTime = microtime(true); + $this->stdout .= $line; + } + + /** + * Adds a line to the STDERR stream. + * + * @param string $line The line to append + */ + public function addErrorOutput($line) + { + $this->lastOutputTime = microtime(true); + $this->stderr .= $line; + } + + /** + * Gets the command line to be executed. + * + * @return string The command to execute + */ + public function getCommandLine() + { + return $this->commandline; + } + + /** + * Sets the command line to be executed. + * + * @param string $commandline The command to execute + * + * @return self The current Process instance + */ + public function setCommandLine($commandline) + { + $this->commandline = $commandline; + + return $this; + } + + /** + * Gets the process timeout (max. runtime). + * + * @return float|null The timeout in seconds or null if it's disabled + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Gets the process idle timeout (max. time since last output). + * + * @return float|null The timeout in seconds or null if it's disabled + */ + public function getIdleTimeout() + { + return $this->idleTimeout; + } + + /** + * Sets the process timeout (max. runtime). + * + * To disable the timeout, set this value to null. + * + * @param int|float|null $timeout The timeout in seconds + * + * @return self The current Process instance + * + * @throws InvalidArgumentException if the timeout is negative + */ + public function setTimeout($timeout) + { + $this->timeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * Sets the process idle timeout (max. time since last output). + * + * To disable the timeout, set this value to null. + * + * @param int|float|null $timeout The timeout in seconds + * + * @return self The current Process instance. + * + * @throws InvalidArgumentException if the timeout is negative + */ + public function setIdleTimeout($timeout) + { + $this->idleTimeout = $this->validateTimeout($timeout); + + return $this; + } + + /** + * Enables or disables the TTY mode. + * + * @param bool $tty True to enabled and false to disable + * + * @return self The current Process instance + * + * @throws RuntimeException In case the TTY mode is not supported + */ + public function setTty($tty) + { + if (defined('PHP_WINDOWS_VERSION_BUILD') && $tty) { + throw new RuntimeException('TTY mode is not supported on Windows platform.'); + } + + $this->tty = (bool) $tty; + + return $this; + } + + /** + * Checks if the TTY mode is enabled. + * + * @return bool true if the TTY mode is enabled, false otherwise + */ + public function isTty() + { + return $this->tty; + } + + /** + * Gets the working directory. + * + * @return string|null The current working directory or null on failure + */ + public function getWorkingDirectory() + { + if (null === $this->cwd) { + // getcwd() will return false if any one of the parent directories does not have + // the readable or search mode set, even if the current directory does + return getcwd() ?: null; + } + + return $this->cwd; + } + + /** + * Sets the current working directory. + * + * @param string $cwd The new working directory + * + * @return self The current Process instance + */ + public function setWorkingDirectory($cwd) + { + $this->cwd = $cwd; + + return $this; + } + + /** + * Gets the environment variables. + * + * @return array The current environment variables + */ + public function getEnv() + { + return $this->env; + } + + /** + * Sets the environment variables. + * + * An environment variable value should be a string. + * If it is an array, the variable is ignored. + * + * That happens in PHP when 'argv' is registered into + * the $_ENV array for instance. + * + * @param array $env The new environment variables + * + * @return self The current Process instance + */ + public function setEnv(array $env) + { + // Process can not handle env values that are arrays + $env = array_filter($env, function ($value) { + return !is_array($value); + }); + + $this->env = array(); + foreach ($env as $key => $value) { + $this->env[(binary) $key] = (binary) $value; + } + + return $this; + } + + /** + * Gets the contents of STDIN. + * + * @return string|null The current contents + */ + public function getStdin() + { + return $this->stdin; + } + + /** + * Sets the contents of STDIN. + * + * @param string|null $stdin The new contents + * + * @return self The current Process instance + * + * @throws LogicException In case the process is running + */ + public function setStdin($stdin) + { + if ($this->isRunning()) { + throw new LogicException('STDIN can not be set while the process is running.'); + } + + $this->stdin = $stdin; + + return $this; + } + + /** + * Gets the options for proc_open. + * + * @return array The current options + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets the options for proc_open. + * + * @param array $options The new options + * + * @return self The current Process instance + */ + public function setOptions(array $options) + { + $this->options = $options; + + return $this; + } + + /** + * Gets whether or not Windows compatibility is enabled. + * + * This is true by default. + * + * @return bool + */ + public function getEnhanceWindowsCompatibility() + { + return $this->enhanceWindowsCompatibility; + } + + /** + * Sets whether or not Windows compatibility is enabled. + * + * @param bool $enhance + * + * @return self The current Process instance + */ + public function setEnhanceWindowsCompatibility($enhance) + { + $this->enhanceWindowsCompatibility = (bool) $enhance; + + return $this; + } + + /** + * Returns whether sigchild compatibility mode is activated or not. + * + * @return bool + */ + public function getEnhanceSigchildCompatibility() + { + return $this->enhanceSigchildCompatibility; + } + + /** + * Activates sigchild compatibility mode. + * + * Sigchild compatibility mode is required to get the exit code and + * determine the success of a process when PHP has been compiled with + * the --enable-sigchild option + * + * @param bool $enhance + * + * @return self The current Process instance + */ + public function setEnhanceSigchildCompatibility($enhance) + { + $this->enhanceSigchildCompatibility = (bool) $enhance; + + return $this; + } + + /** + * Performs a check between the timeout definition and the time the process started. + * + * In case you run a background process (with the start method), you should + * trigger this method regularly to ensure the process timeout + * + * @throws ProcessTimedOutException In case the timeout was reached + */ + public function checkTimeout() + { + if ($this->status !== self::STATUS_STARTED) { + return; + } + + if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) { + $this->stop(0); + + throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL); + } + + if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) { + $this->stop(0); + + throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE); + } + } + + /** + * Creates the descriptors needed by the proc_open. + * + * @return array + */ + private function getDescriptors() + { + $this->processPipes = new ProcessPipes($this->useFileHandles, $this->tty); + $descriptors = $this->processPipes->getDescriptors(); + + if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { + // last exit code is output on the fourth pipe and caught to work around --enable-sigchild + $descriptors = array_merge($descriptors, array(array('pipe', 'w'))); + + $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code'; + } + + return $descriptors; + } + + /** + * Builds up the callback used by wait(). + * + * The callbacks adds all occurred output to the specific buffer and calls + * the user callback (if present) with the received output. + * + * @param callable|null $callback The user defined PHP callback + * + * @return callable A PHP callable + */ + protected function buildCallback($callback) + { + $that = $this; + $out = self::OUT; + $err = self::ERR; + $callback = function ($type, $data) use ($that, $callback, $out, $err) { + if ($out == $type) { + $that->addOutput($data); + } else { + $that->addErrorOutput($data); + } + + if (null !== $callback) { + call_user_func($callback, $type, $data); + } + }; + + return $callback; + } + + /** + * Updates the status of the process, reads pipes. + * + * @param bool $blocking Whether to use a blocking read call. + */ + protected function updateStatus($blocking) + { + if (self::STATUS_STARTED !== $this->status) { + return; + } + + $this->processInformation = proc_get_status($this->process); + $this->captureExitCode(); + + $this->readPipes($blocking, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); + + if (!$this->processInformation['running']) { + $this->close(); + } + } + + /** + * Returns whether PHP has been compiled with the '--enable-sigchild' option or not. + * + * @return bool + */ + protected function isSigchildEnabled() + { + if (null !== self::$sigchild) { + return self::$sigchild; + } + + if (!function_exists('phpinfo')) { + return self::$sigchild = false; + } + + ob_start(); + phpinfo(INFO_GENERAL); + + return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); + } + + /** + * Validates and returns the filtered timeout. + * + * @param int|float|null $timeout + * + * @return float|null + */ + private function validateTimeout($timeout) + { + $timeout = (float) $timeout; + + if (0.0 === $timeout) { + $timeout = null; + } elseif ($timeout < 0) { + throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); + } + + return $timeout; + } + + /** + * Reads pipes, executes callback. + * + * @param bool $blocking Whether to use blocking calls or not. + * @param bool $close Whether to close file handles or not. + */ + private function readPipes($blocking, $close) + { + if ($close) { + $result = $this->processPipes->readAndCloseHandles($blocking); + } else { + $result = $this->processPipes->read($blocking); + } + + foreach ($result as $type => $data) { + if (3 == $type) { + $this->fallbackExitcode = (int) $data; + } else { + call_user_func($this->callback, $type === self::STDOUT ? self::OUT : self::ERR, $data); + } + } + } + + /** + * Captures the exitcode if mentioned in the process information. + */ + private function captureExitCode() + { + if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) { + $this->exitcode = $this->processInformation['exitcode']; + } + } + + /** + * Closes process resource, closes file handles, sets the exitcode. + * + * @return int The exitcode + */ + private function close() + { + $this->processPipes->close(); + if (is_resource($this->process)) { + $exitcode = proc_close($this->process); + } else { + $exitcode = -1; + } + + $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1); + $this->status = self::STATUS_TERMINATED; + + if (-1 === $this->exitcode && null !== $this->fallbackExitcode) { + $this->exitcode = $this->fallbackExitcode; + } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { + // if process has been signaled, no exitcode but a valid termsig, apply Unix convention + $this->exitcode = 128 + $this->processInformation['termsig']; + } + + return $this->exitcode; + } + + /** + * Resets data related to the latest run of the process. + */ + private function resetProcessData() + { + $this->starttime = null; + $this->callback = null; + $this->exitcode = null; + $this->fallbackExitcode = null; + $this->processInformation = null; + $this->stdout = null; + $this->stderr = null; + $this->process = null; + $this->status = self::STATUS_READY; + $this->incrementalOutputOffset = 0; + $this->incrementalErrorOutputOffset = 0; + } + + /** + * Sends a POSIX signal to the process. + * + * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php) + * @param bool $throwException Whether to throw exception in case signal failed + * + * @return bool True if the signal was sent successfully, false otherwise + * + * @throws LogicException In case the process is not running + * @throws RuntimeException In case --enable-sigchild is activated + * @throws RuntimeException In case of failure + */ + private function doSignal($signal, $throwException) + { + if (!$this->isRunning()) { + if ($throwException) { + throw new LogicException('Can not send signal on a non running process.'); + } + + return false; + } + + if ($this->isSigchildEnabled()) { + if ($throwException) { + throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + } + + return false; + } + + if (true !== @proc_terminate($this->process, $signal)) { + if ($throwException) { + throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); + } + + return false; + } + + return true; + } + + /** + * Ensures the process is running or terminated, throws a LogicException if the process has a not started. + * + * @param string $functionName The function name that was called. + * + * @throws LogicException If the process has not run. + */ + private function requireProcessIsStarted($functionName) + { + if (!$this->isStarted()) { + throw new LogicException(sprintf('Process must be started before calling %s.', $functionName)); + } + } + + /** + * Ensures the process is terminated, throws a LogicException if the process has a status different than `terminated`. + * + * @param string $functionName The function name that was called. + * + * @throws LogicException If the process is not yet terminated. + */ + private function requireProcessIsTerminated($functionName) + { + if (!$this->isTerminated()) { + throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName)); + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php new file mode 100644 index 000000000..c9a77d448 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php @@ -0,0 +1,241 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\InvalidArgumentException; +use Symfony\Component\Process\Exception\LogicException; + +/** + * Process builder. + * + * @author Kris Wallsmith + */ +class ProcessBuilder +{ + private $arguments; + private $cwd; + private $env = array(); + private $stdin; + private $timeout = 60; + private $options = array(); + private $inheritEnv = true; + private $prefix = array(); + + /** + * Constructor + * + * @param string[] $arguments An array of arguments + */ + public function __construct(array $arguments = array()) + { + $this->arguments = $arguments; + } + + /** + * Creates a process builder instance. + * + * @param string[] $arguments An array of arguments + * + * @return ProcessBuilder + */ + public static function create(array $arguments = array()) + { + return new static($arguments); + } + + /** + * Adds an unescaped argument to the command string. + * + * @param string $argument A command argument + * + * @return ProcessBuilder + */ + public function add($argument) + { + $this->arguments[] = $argument; + + return $this; + } + + /** + * Adds an unescaped prefix to the command string. + * + * The prefix is preserved when resetting arguments. + * + * @param string|array $prefix A command prefix or an array of command prefixes + * + * @return ProcessBuilder + */ + public function setPrefix($prefix) + { + $this->prefix = is_array($prefix) ? $prefix : array($prefix); + + return $this; + } + + /** + * Sets the arguments of the process. + * + * Arguments must not be escaped. + * Previous arguments are removed. + * + * @param string[] $arguments + * + * @return ProcessBuilder + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + + /** + * Sets the working directory. + * + * @param null|string $cwd The working directory + * + * @return ProcessBuilder + */ + public function setWorkingDirectory($cwd) + { + $this->cwd = $cwd; + + return $this; + } + + /** + * Sets whether environment variables will be inherited or not. + * + * @param bool $inheritEnv + * + * @return ProcessBuilder + */ + public function inheritEnvironmentVariables($inheritEnv = true) + { + $this->inheritEnv = $inheritEnv; + + return $this; + } + + /** + * Sets an environment variable + * + * Setting a variable overrides its previous value. Use `null` to unset a + * defined environment variable. + * + * @param string $name The variable name + * @param null|string $value The variable value + * + * @return ProcessBuilder + */ + public function setEnv($name, $value) + { + $this->env[$name] = $value; + + return $this; + } + + public function addEnvironmentVariables(array $variables) + { + $this->env = array_replace($this->env, $variables); + + return $this; + } + + /** + * Sets the input of the process. + * + * @param string $stdin The input as a string + * + * @return ProcessBuilder + */ + public function setInput($stdin) + { + $this->stdin = $stdin; + + return $this; + } + + /** + * Sets the process timeout. + * + * To disable the timeout, set this value to null. + * + * @param float|null + * + * @return ProcessBuilder + * + * @throws InvalidArgumentException + */ + public function setTimeout($timeout) + { + if (null === $timeout) { + $this->timeout = null; + + return $this; + } + + $timeout = (float) $timeout; + + if ($timeout < 0) { + throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); + } + + $this->timeout = $timeout; + + return $this; + } + + /** + * Adds a proc_open option. + * + * @param string $name The option name + * @param string $value The option value + * + * @return ProcessBuilder + */ + public function setOption($name, $value) + { + $this->options[$name] = $value; + + return $this; + } + + /** + * Creates a Process instance and returns it. + * + * @return Process + * + * @throws LogicException In case no arguments have been provided + */ + public function getProcess() + { + if (0 === count($this->prefix) && 0 === count($this->arguments)) { + throw new LogicException('You must add() command arguments before calling getProcess().'); + } + + $options = $this->options; + + $arguments = array_merge($this->prefix, $this->arguments); + $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments)); + + if ($this->inheritEnv) { + // include $_ENV for BC purposes + $env = array_replace($_ENV, $_SERVER, $this->env); + } else { + $env = $this->env; + } + + return new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php new file mode 100644 index 000000000..f35d1c128 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php @@ -0,0 +1,358 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +use Symfony\Component\Process\Exception\RuntimeException; + +/** + * ProcessPipes manages descriptors and pipes for the use of proc_open. + */ +class ProcessPipes +{ + /** @var array */ + public $pipes = array(); + /** @var array */ + private $files = array(); + /** @var array */ + private $fileHandles = array(); + /** @var array */ + private $readBytes = array(); + /** @var bool */ + private $useFiles; + /** @var bool */ + private $ttyMode; + + const CHUNK_SIZE = 16384; + + public function __construct($useFiles, $ttyMode) + { + $this->useFiles = (bool) $useFiles; + $this->ttyMode = (bool) $ttyMode; + + // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. + // Workaround for this problem is to use temporary files instead of pipes on Windows platform. + // + // @see https://bugs.php.net/bug.php?id=51800 + if ($this->useFiles) { + $this->files = array( + Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'), + Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'), + ); + foreach ($this->files as $offset => $file) { + $this->fileHandles[$offset] = fopen($this->files[$offset], 'rb'); + if (false === $this->fileHandles[$offset]) { + throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); + } + } + $this->readBytes = array( + Process::STDOUT => 0, + Process::STDERR => 0, + ); + } + } + + public function __destruct() + { + $this->close(); + $this->removeFiles(); + } + + /** + * Sets non-blocking mode on pipes. + */ + public function unblock() + { + foreach ($this->pipes as $pipe) { + stream_set_blocking($pipe, 0); + } + } + + /** + * Closes file handles and pipes. + */ + public function close() + { + $this->closeUnixPipes(); + foreach ($this->fileHandles as $handle) { + fclose($handle); + } + $this->fileHandles = array(); + } + + /** + * Closes Unix pipes. + * + * Nothing happens in case file handles are used. + */ + public function closeUnixPipes() + { + foreach ($this->pipes as $pipe) { + fclose($pipe); + } + $this->pipes = array(); + } + + /** + * Returns an array of descriptors for the use of proc_open. + * + * @return array + */ + public function getDescriptors() + { + if ($this->useFiles) { + // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800) + // We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650 + // So we redirect output within the commandline and pass the nul device to the process + return array( + array('pipe', 'r'), + array('file', 'NUL', 'w'), + array('file', 'NUL', 'w'), + ); + } + + if ($this->ttyMode) { + return array( + array('file', '/dev/tty', 'r'), + array('file', '/dev/tty', 'w'), + array('file', '/dev/tty', 'w'), + ); + } + + return array( + array('pipe', 'r'), // stdin + array('pipe', 'w'), // stdout + array('pipe', 'w'), // stderr + ); + } + + /** + * Returns an array of filenames indexed by their related stream in case these pipes use temporary files. + * + * @return array + */ + public function getFiles() + { + if ($this->useFiles) { + return $this->files; + } + + return array(); + } + + /** + * Reads data in file handles and pipes. + * + * @param bool $blocking Whether to use blocking calls or not. + * + * @return array An array of read data indexed by their fd. + */ + public function read($blocking) + { + return array_replace($this->readStreams($blocking), $this->readFileHandles()); + } + + /** + * Reads data in file handles and pipes, closes them if EOF is reached. + * + * @param bool $blocking Whether to use blocking calls or not. + * + * @return array An array of read data indexed by their fd. + */ + public function readAndCloseHandles($blocking) + { + return array_replace($this->readStreams($blocking, true), $this->readFileHandles(true)); + } + + /** + * Returns if the current state has open file handles or pipes. + * + * @return bool + */ + public function hasOpenHandles() + { + if (!$this->useFiles) { + return (bool) $this->pipes; + } + + return (bool) $this->pipes && (bool) $this->fileHandles; + } + + /** + * Writes stdin data. + * + * @param bool $blocking Whether to use blocking calls or not. + * @param string|null $stdin The data to write. + */ + public function write($blocking, $stdin) + { + if (null === $stdin) { + fclose($this->pipes[0]); + unset($this->pipes[0]); + + return; + } + + $writePipes = array($this->pipes[0]); + unset($this->pipes[0]); + $stdinLen = strlen($stdin); + $stdinOffset = 0; + + while ($writePipes) { + $r = null; + $w = $writePipes; + $e = null; + + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { + // if a system call has been interrupted, forget about it, let's try again + if ($this->hasSystemCallBeenInterrupted()) { + continue; + } + break; + } + + // nothing has changed, let's wait until the process is ready + if (0 === $n) { + continue; + } + + if ($w) { + $written = fwrite($writePipes[0], (binary) substr($stdin, $stdinOffset), 8192); + if (false !== $written) { + $stdinOffset += $written; + } + if ($stdinOffset >= $stdinLen) { + fclose($writePipes[0]); + $writePipes = null; + } + } + } + } + + /** + * Reads data in file handles. + * + * @param bool $close Whether to close file handles or not. + * + * @return array An array of read data indexed by their fd. + */ + private function readFileHandles($close = false) + { + $read = array(); + $fh = $this->fileHandles; + foreach ($fh as $type => $fileHandle) { + if (0 !== fseek($fileHandle, $this->readBytes[$type])) { + continue; + } + $data = ''; + $dataread = null; + while (!feof($fileHandle)) { + if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) { + $data .= $dataread; + } + } + if (0 < $length = strlen($data)) { + $this->readBytes[$type] += $length; + $read[$type] = $data; + } + + if (false === $dataread || (true === $close && feof($fileHandle) && '' === $data)) { + fclose($this->fileHandles[$type]); + unset($this->fileHandles[$type]); + } + } + + return $read; + } + + /** + * Reads data in file pipes streams. + * + * @param bool $blocking Whether to use blocking calls or not. + * @param bool $close Whether to close file handles or not. + * + * @return array An array of read data indexed by their fd. + */ + private function readStreams($blocking, $close = false) + { + if (empty($this->pipes)) { + return array(); + } + + $read = array(); + + $r = $this->pipes; + $w = null; + $e = null; + + // let's have a look if something changed in streams + if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { + // if a system call has been interrupted, forget about it, let's try again + // otherwise, an error occurred, let's reset pipes + if (!$this->hasSystemCallBeenInterrupted()) { + $this->pipes = array(); + } + + return $read; + } + + // nothing has changed + if (0 === $n) { + return $read; + } + + foreach ($r as $pipe) { + $type = array_search($pipe, $this->pipes); + + $data = ''; + while ($dataread = fread($pipe, self::CHUNK_SIZE)) { + $data .= $dataread; + } + + if ($data) { + $read[$type] = $data; + } + + if (false === $data || (true === $close && feof($pipe) && '' === $data)) { + fclose($this->pipes[$type]); + unset($this->pipes[$type]); + } + } + + return $read; + } + + /** + * Returns true if a system call has been interrupted. + * + * @return bool + */ + private function hasSystemCallBeenInterrupted() + { + $lastError = error_get_last(); + + // stream_select returns false when the `select` system call is interrupted by an incoming signal + return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); + } + + /** + * Removes temporary files + */ + private function removeFiles() + { + foreach ($this->files as $filename) { + if (file_exists($filename)) { + @unlink($filename); + } + } + $this->files = array(); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php new file mode 100644 index 000000000..5317cd088 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process; + +/** + * ProcessUtils is a bunch of utility methods. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Martin Hasoň + */ +class ProcessUtils +{ + /** + * This class should not be instantiated + */ + private function __construct() + { + } + + /** + * Escapes a string to be used as a shell argument. + * + * @param string $argument The argument that will be escaped + * + * @return string The escaped argument + */ + public static function escapeArgument($argument) + { + //Fix for PHP bug #43784 escapeshellarg removes % from given string + //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows + //@see https://bugs.php.net/bug.php?id=43784 + //@see https://bugs.php.net/bug.php?id=49446 + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + if ('' === $argument) { + return escapeshellarg($argument); + } + + $escapedArgument = ''; + $quote = false; + foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { + if ('"' === $part) { + $escapedArgument .= '\\"'; + } elseif (self::isSurroundedBy($part, '%')) { + // Avoid environment variable expansion + $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%'; + } else { + // escape trailing backslash + if ('\\' === substr($part, -1)) { + $part .= '\\'; + } + $quote = true; + $escapedArgument .= $part; + } + } + if ($quote) { + $escapedArgument = '"'.$escapedArgument.'"'; + } + + return $escapedArgument; + } + + return escapeshellarg($argument); + } + + private static function isSurroundedBy($arg, $char) + { + return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/README.md b/core/lib/symfony/process/Symfony/Component/Process/README.md new file mode 100644 index 000000000..9bcc656b7 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/README.md @@ -0,0 +1,47 @@ +Process Component +================= + +Process executes commands in sub-processes. + +In this example, we run a simple directory listing and get the result back: + + use Symfony\Component\Process\Process; + + $process = new Process('ls -lsa'); + $process->setTimeout(3600); + $process->run(); + if (!$process->isSuccessful()) { + throw new RuntimeException($process->getErrorOutput()); + } + + print $process->getOutput(); + +You can think that this is easy to achieve with plain PHP but it's not especially +if you want to take care of the subtle differences between the different platforms. + +And if you want to be able to get some feedback in real-time, just pass an +anonymous function to the ``run()`` method and you will get the output buffer +as it becomes available: + + use Symfony\Component\Process\Process; + + $process = new Process('ls -lsa'); + $process->run(function ($type, $buffer) { + if ('err' === $type) { + echo 'ERR > '.$buffer; + } else { + echo 'OUT > '.$buffer; + } + }); + +That's great if you want to execute a long running command (like rsync-ing files to a +remote server) and give feedback to the user in real-time. + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/XXX/ + $ composer.phar install + $ phpunit diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php new file mode 100644 index 000000000..9522bd402 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php @@ -0,0 +1,873 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\Exception\ProcessTimedOutException; +use Symfony\Component\Process\Exception\LogicException; +use Symfony\Component\Process\Process; +use Symfony\Component\Process\Exception\RuntimeException; +use Symfony\Component\Process\ProcessPipes; + +/** + * @author Robert Schönthal + */ +abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase +{ + public function testThatProcessDoesNotThrowWarningDuringRun() + { + @trigger_error('Test Error', E_USER_NOTICE); + $process = $this->getProcess("php -r 'sleep(3)'"); + $process->run(); + $actualError = error_get_last(); + $this->assertEquals('Test Error', $actualError['message']); + $this->assertEquals(E_USER_NOTICE, $actualError['type']); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + */ + public function testNegativeTimeoutFromConstructor() + { + $this->getProcess('', null, null, null, -1); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + */ + public function testNegativeTimeoutFromSetter() + { + $p = $this->getProcess(''); + $p->setTimeout(-1); + } + + public function testFloatAndNullTimeout() + { + $p = $this->getProcess(''); + + $p->setTimeout(10); + $this->assertSame(10.0, $p->getTimeout()); + + $p->setTimeout(null); + $this->assertNull($p->getTimeout()); + + $p->setTimeout(0.0); + $this->assertNull($p->getTimeout()); + } + + public function testStopWithTimeoutIsActuallyWorking() + { + $this->verifyPosixIsEnabled(); + + // exec is mandatory here since we send a signal to the process + // see https://github.com/symfony/symfony/issues/5030 about prepending + // command with exec + $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3'); + $p->start(); + usleep(100000); + $start = microtime(true); + $p->stop(1.1, SIGKILL); + while ($p->isRunning()) { + usleep(1000); + } + $duration = microtime(true) - $start; + + $this->assertLessThan(1.8, $duration); + } + + public function testAllOutputIsActuallyReadOnTermination() + { + // this code will result in a maximum of 2 reads of 8192 bytes by calling + // start() and isRunning(). by the time getOutput() is called the process + // has terminated so the internal pipes array is already empty. normally + // the call to start() will not read any data as the process will not have + // generated output, but this is non-deterministic so we must count it as + // a possibility. therefore we need 2 * ProcessPipes::CHUNK_SIZE plus + // another byte which will never be read. + $expectedOutputSize = ProcessPipes::CHUNK_SIZE * 2 + 2; + + $code = sprintf('echo str_repeat(\'*\', %d);', $expectedOutputSize); + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); + + $p->start(); + // Let's wait enough time for process to finish... + // Here we don't call Process::run or Process::wait to avoid any read of pipes + usleep(500000); + + if ($p->isRunning()) { + $this->markTestSkipped('Process execution did not complete in the required time frame'); + } + + $o = $p->getOutput(); + + $this->assertEquals($expectedOutputSize, strlen($o)); + } + + public function testCallbacksAreExecutedWithStart() + { + $data = ''; + + $process = $this->getProcess('echo foo && php -r "sleep(1);" && echo foo'); + $process->start(function ($type, $buffer) use (&$data) { + $data .= $buffer; + }); + + while ($process->isRunning()) { + usleep(10000); + } + + $this->assertEquals(2, preg_match_all('/foo/', $data, $matches)); + } + + /** + * tests results from sub processes + * + * @dataProvider responsesCodeProvider + */ + public function testProcessResponses($expected, $getter, $code) + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); + $p->run(); + + $this->assertSame($expected, $p->$getter()); + } + + /** + * tests results from sub processes + * + * @dataProvider pipesCodeProvider + */ + public function testProcessPipes($code, $size) + { + $expected = str_repeat(str_repeat('*', 1024), $size) . '!'; + $expectedLength = (1024 * $size) + 1; + + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); + $p->setStdin($expected); + $p->run(); + + $this->assertEquals($expectedLength, strlen($p->getOutput())); + $this->assertEquals($expectedLength, strlen($p->getErrorOutput())); + } + + public function testSetStdinWhileRunningThrowsAnException() + { + $process = $this->getProcess('php -r "usleep(500000);"'); + $process->start(); + try { + $process->setStdin('foobar'); + $process->stop(); + $this->fail('A LogicException should have been raised.'); + } catch (LogicException $e) { + $this->assertEquals('STDIN can not be set while the process is running.', $e->getMessage()); + } + $process->stop(); + } + + public function chainedCommandsOutputProvider() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + return array( + array("2 \r\n2\r\n", '&&', '2') + ); + } + + return array( + array("1\n1\n", ';', '1'), + array("2\n2\n", '&&', '2'), + ); + } + + /** + * + * @dataProvider chainedCommandsOutputProvider + */ + public function testChainedCommandsOutput($expected, $operator, $input) + { + $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input)); + $process->run(); + $this->assertEquals($expected, $process->getOutput()); + } + + public function testCallbackIsExecutedForOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('echo \'foo\';'))); + + $called = false; + $p->run(function ($type, $buffer) use (&$called) { + $called = $buffer === 'foo'; + }); + + $this->assertTrue($called, 'The callback should be executed with the output'); + } + + public function testGetErrorOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); + + $p->run(); + $this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches)); + } + + public function testGetIncrementalErrorOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(100000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); + + $p->start(); + while ($p->isRunning()) { + $this->assertLessThanOrEqual(1, preg_match_all('/ERROR/', $p->getIncrementalErrorOutput(), $matches)); + usleep(20000); + } + } + + public function testFlushErrorOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); + + $p->run(); + $p->clearErrorOutput(); + $this->assertEmpty($p->getErrorOutput()); + } + + public function testGetOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }'))); + + $p->run(); + $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches)); + } + + public function testGetIncrementalOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }'))); + + $p->start(); + while ($p->isRunning()) { + $this->assertLessThanOrEqual(1, preg_match_all('/foo/', $p->getIncrementalOutput(), $matches)); + usleep(20000); + } + } + + public function testFlushOutput() + { + $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}'))); + + $p->run(); + $p->clearOutput(); + $this->assertEmpty($p->getOutput()); + } + + public function testExitCodeCommandFailed() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX exit code'); + } + + // such command run in bash return an exitcode 127 + $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis'); + $process->run(); + + $this->assertGreaterThan(0, $process->getExitCode()); + } + + public function testTTYCommand() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does have /dev/tty support'); + } + + $process = $this->getProcess('echo "foo" >> /dev/null && php -r "usleep(100000);"'); + $process->setTty(true); + $process->start(); + $this->assertTrue($process->isRunning()); + $process->wait(); + + $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); + } + + public function testTTYCommandExitCode() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does have /dev/tty support'); + } + + $process = $this->getProcess('echo "foo" >> /dev/null'); + $process->setTty(true); + $process->run(); + + $this->assertTrue($process->isSuccessful()); + } + + public function testTTYInWindowsEnvironment() + { + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('This test is for Windows platform only'); + } + + $process = $this->getProcess('echo "foo" >> /dev/null'); + $process->setTty(false); + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'TTY mode is not supported on Windows platform.'); + $process->setTty(true); + } + + public function testExitCodeTextIsNullWhenExitCodeIsNull() + { + $process = $this->getProcess(''); + $this->assertNull($process->getExitCodeText()); + } + + public function testExitCodeText() + { + $process = $this->getProcess(''); + $r = new \ReflectionObject($process); + $p = $r->getProperty('exitcode'); + $p->setAccessible(true); + + $p->setValue($process, 2); + $this->assertEquals('Misuse of shell builtins', $process->getExitCodeText()); + } + + public function testStartIsNonBlocking() + { + $process = $this->getProcess('php -r "usleep(500000);"'); + $start = microtime(true); + $process->start(); + $end = microtime(true); + $this->assertLessThan(0.2, $end-$start); + $process->wait(); + } + + public function testUpdateStatus() + { + $process = $this->getProcess('php -h'); + $process->run(); + $this->assertTrue(strlen($process->getOutput()) > 0); + } + + public function testGetExitCodeIsNullOnStart() + { + $process = $this->getProcess('php -r "usleep(200000);"'); + $this->assertNull($process->getExitCode()); + $process->start(); + $this->assertNull($process->getExitCode()); + $process->wait(); + $this->assertEquals(0, $process->getExitCode()); + } + + public function testGetExitCodeIsNullOnWhenStartingAgain() + { + $process = $this->getProcess('php -r "usleep(200000);"'); + $process->run(); + $this->assertEquals(0, $process->getExitCode()); + $process->start(); + $this->assertNull($process->getExitCode()); + $process->wait(); + $this->assertEquals(0, $process->getExitCode()); + } + + public function testGetExitCode() + { + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertSame(0, $process->getExitCode()); + } + + public function testStatus() + { + $process = $this->getProcess('php -r "usleep(500000);"'); + $this->assertFalse($process->isRunning()); + $this->assertFalse($process->isStarted()); + $this->assertFalse($process->isTerminated()); + $this->assertSame(Process::STATUS_READY, $process->getStatus()); + $process->start(); + $this->assertTrue($process->isRunning()); + $this->assertTrue($process->isStarted()); + $this->assertFalse($process->isTerminated()); + $this->assertSame(Process::STATUS_STARTED, $process->getStatus()); + $process->wait(); + $this->assertFalse($process->isRunning()); + $this->assertTrue($process->isStarted()); + $this->assertTrue($process->isTerminated()); + $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); + } + + public function testStop() + { + $process = $this->getProcess('php -r "sleep(4);"'); + $process->start(); + $this->assertTrue($process->isRunning()); + $process->stop(); + $this->assertFalse($process->isRunning()); + } + + public function testIsSuccessful() + { + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertTrue($process->isSuccessful()); + } + + public function testIsSuccessfulOnlyAfterTerminated() + { + $process = $this->getProcess('php -r "sleep(1);"'); + $process->start(); + while ($process->isRunning()) { + $this->assertFalse($process->isSuccessful()); + usleep(300000); + } + + $this->assertTrue($process->isSuccessful()); + } + + public function testIsNotSuccessful() + { + $process = $this->getProcess('php -r "usleep(500000);throw new \Exception(\'BOUM\');"'); + $process->start(); + $this->assertTrue($process->isRunning()); + $process->wait(); + $this->assertFalse($process->isSuccessful()); + } + + public function testProcessIsNotSignaled() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertFalse($process->hasBeenSignaled()); + } + + public function testProcessWithoutTermSignalIsNotSignaled() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertFalse($process->hasBeenSignaled()); + } + + public function testProcessWithoutTermSignal() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertEquals(0, $process->getTermSignal()); + } + + public function testProcessIsSignaledIfStopped() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + $process = $this->getProcess('php -r "sleep(4);"'); + $process->start(); + $process->stop(); + $this->assertTrue($process->hasBeenSignaled()); + } + + public function testProcessWithTermSignal() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + // SIGTERM is only defined if pcntl extension is present + $termSignal = defined('SIGTERM') ? SIGTERM : 15; + + $process = $this->getProcess('php -r "sleep(4);"'); + $process->start(); + $process->stop(); + + $this->assertEquals($termSignal, $process->getTermSignal()); + } + + public function testProcessThrowsExceptionWhenExternallySignaled() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Windows does not support POSIX signals'); + } + + if (!function_exists('posix_kill')) { + $this->markTestSkipped('posix_kill is required for this test'); + } + + $termSignal = defined('SIGKILL') ? SIGKILL : 9; + + $process = $this->getProcess('exec php -r "while (true) {}"'); + $process->start(); + posix_kill($process->getPid(), $termSignal); + + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'The process has been signaled with signal "9".'); + $process->wait(); + } + + public function testRestart() + { + $process1 = $this->getProcess('php -r "echo getmypid();"'); + $process1->run(); + $process2 = $process1->restart(); + + $process2->wait(); // wait for output + + // Ensure that both processed finished and the output is numeric + $this->assertFalse($process1->isRunning()); + $this->assertFalse($process2->isRunning()); + $this->assertTrue(is_numeric($process1->getOutput())); + $this->assertTrue(is_numeric($process2->getOutput())); + + // Ensure that restart returned a new process by check that the output is different + $this->assertNotEquals($process1->getOutput(), $process2->getOutput()); + } + + public function testPhpDeadlock() + { + $this->markTestSkipped('Can course PHP to hang'); + + // Sleep doesn't work as it will allow the process to handle signals and close + // file handles from the other end. + $process = $this->getProcess('php -r "while (true) {}"'); + $process->start(); + + // PHP will deadlock when it tries to cleanup $process + } + + public function testRunProcessWithTimeout() + { + $timeout = 0.5; + $process = $this->getProcess('php -r "usleep(600000);"'); + $process->setTimeout($timeout); + $start = microtime(true); + try { + $process->run(); + $this->fail('A RuntimeException should have been raised'); + } catch (RuntimeException $e) { + + } + $duration = microtime(true) - $start; + + $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration); + } + + public function testCheckTimeoutOnNonStartedProcess() + { + $process = $this->getProcess('php -r "sleep(3);"'); + $process->checkTimeout(); + } + + public function testCheckTimeoutOnTerminatedProcess() + { + $process = $this->getProcess('php -v'); + $process->run(); + $process->checkTimeout(); + } + + public function testCheckTimeoutOnStartedProcess() + { + $timeout = 0.5; + $precision = 100000; + $process = $this->getProcess('php -r "sleep(3);"'); + $process->setTimeout($timeout); + $start = microtime(true); + + $process->start(); + + try { + while ($process->isRunning()) { + $process->checkTimeout(); + usleep($precision); + } + $this->fail('A RuntimeException should have been raised'); + } catch (RuntimeException $e) { + + } + $duration = microtime(true) - $start; + + $this->assertLessThan($timeout + $precision, $duration); + $this->assertFalse($process->isSuccessful()); + } + + /** + * @group idle-timeout + */ + public function testIdleTimeout() + { + $process = $this->getProcess('sleep 3'); + $process->setTimeout(10); + $process->setIdleTimeout(1); + + try { + $process->run(); + + $this->fail('A timeout exception was expected.'); + } catch (ProcessTimedOutException $ex) { + $this->assertTrue($ex->isIdleTimeout()); + $this->assertFalse($ex->isGeneralTimeout()); + $this->assertEquals(1.0, $ex->getExceededTimeout()); + } + } + + /** + * @group idle-timeout + */ + public function testIdleTimeoutNotExceededWhenOutputIsSent() + { + $process = $this->getProcess('echo "foo" && sleep 1 && echo "foo" && sleep 1 && echo "foo" && sleep 1 && echo "foo" && sleep 5'); + $process->setTimeout(5); + $process->setIdleTimeout(3); + + try { + $process->run(); + $this->fail('A timeout exception was expected.'); + } catch (ProcessTimedOutException $ex) { + $this->assertTrue($ex->isGeneralTimeout()); + $this->assertFalse($ex->isIdleTimeout()); + $this->assertEquals(5.0, $ex->getExceededTimeout()); + } + } + + public function testStartAfterATimeout() + { + $process = $this->getProcess('php -r "$n = 1000; while ($n--) {echo \'\'; usleep(1000); }"'); + $process->setTimeout(0.1); + try { + $process->run(); + $this->fail('An exception should have been raised.'); + } catch (\Exception $e) { + + } + $process->start(); + usleep(10000); + $process->stop(); + } + + public function testGetPid() + { + $process = $this->getProcess('php -r "usleep(500000);"'); + $process->start(); + $this->assertGreaterThan(0, $process->getPid()); + $process->wait(); + } + + public function testGetPidIsNullBeforeStart() + { + $process = $this->getProcess('php -r "sleep(1);"'); + $this->assertNull($process->getPid()); + } + + public function testGetPidIsNullAfterRun() + { + $process = $this->getProcess('php -m'); + $process->run(); + $this->assertNull($process->getPid()); + } + + public function testSignal() + { + $this->verifyPosixIsEnabled(); + + $process = $this->getProcess('exec php -f ' . __DIR__ . '/SignalListener.php'); + $process->start(); + usleep(500000); + $process->signal(SIGUSR1); + + while ($process->isRunning() && false === strpos($process->getoutput(), 'Caught SIGUSR1')) { + usleep(10000); + } + + $this->assertEquals('Caught SIGUSR1', $process->getOutput()); + } + + public function testExitCodeIsAvailableAfterSignal() + { + $this->verifyPosixIsEnabled(); + + $process = $this->getProcess('sleep 4'); + $process->start(); + $process->signal(SIGKILL); + + while ($process->isRunning()) { + usleep(10000); + } + + $this->assertFalse($process->isRunning()); + $this->assertTrue($process->hasBeenSignaled()); + $this->assertFalse($process->isSuccessful()); + $this->assertEquals(137, $process->getExitCode()); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\LogicException + */ + public function testSignalProcessNotRunning() + { + $this->verifyPosixIsEnabled(); + $process = $this->getProcess('php -m'); + $process->signal(SIGHUP); + } + + /** + * @dataProvider provideMethodsThatNeedARunningProcess + */ + public function testMethodsThatNeedARunningProcess($method) + { + $process = $this->getProcess('php -m'); + $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method)); + call_user_func(array($process, $method)); + } + + public function provideMethodsThatNeedARunningProcess() + { + return array( + array('getOutput'), + array('getIncrementalOutput'), + array('getErrorOutput'), + array('getIncrementalErrorOutput'), + array('wait'), + ); + } + + /** + * @dataProvider provideMethodsThatNeedATerminatedProcess + */ + public function testMethodsThatNeedATerminatedProcess($method) + { + $process = $this->getProcess('php -r "sleep(1);"'); + $process->start(); + try { + call_user_func(array($process, $method)); + $process->stop(0); + $this->fail('A LogicException must have been thrown'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Process\Exception\LogicException', $e); + $this->assertEquals(sprintf('Process must be terminated before calling %s.', $method), $e->getMessage()); + } + $process->stop(0); + } + + public function provideMethodsThatNeedATerminatedProcess() + { + return array( + array('hasBeenSignaled'), + array('getTermSignal'), + array('hasBeenStopped'), + array('getStopSignal'), + ); + } + + private function verifyPosixIsEnabled() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('POSIX signals do not work on Windows'); + } + if (!defined('SIGUSR1')) { + $this->markTestSkipped('The pcntl extension is not enabled'); + } + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + */ + public function testSignalWithWrongIntSignal() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('POSIX signals do not work on Windows'); + } + + $process = $this->getProcess('php -r "sleep(3);"'); + $process->start(); + $process->signal(-4); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + */ + public function testSignalWithWrongNonIntSignal() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('POSIX signals do not work on Windows'); + } + + $process = $this->getProcess('php -r "sleep(3);"'); + $process->start(); + $process->signal('Céphalopodes'); + } + + public function responsesCodeProvider() + { + return array( + //expected output / getter / code to execute + //array(1,'getExitCode','exit(1);'), + //array(true,'isSuccessful','exit();'), + array('output', 'getOutput', 'echo \'output\';'), + ); + } + + public function pipesCodeProvider() + { + $variations = array( + 'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);', + 'include \''.__DIR__.'/PipeStdinInStdoutStdErrStreamSelect.php\';', + ); + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + // Avoid XL buffers on Windows because of https://bugs.php.net/bug.php?id=65650 + $sizes = array(1, 2, 4, 8); + } else { + $sizes = array(1, 16, 64, 1024, 4096); + } + + $codes = array(); + foreach ($sizes as $size) { + foreach ($variations as $code) { + $codes[] = array($code, $size); + } + } + + return $codes; + } + + /** + * provides default method names for simple getter/setter + */ + public function methodProvider() + { + $defaults = array( + array('CommandLine'), + array('Timeout'), + array('WorkingDirectory'), + array('Env'), + array('Stdin'), + array('Options') + ); + + return $defaults; + } + + /** + * @param string $commandline + * @param null $cwd + * @param array $env + * @param null $stdin + * @param int $timeout + * @param array $options + * + * @return Process + */ + abstract protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()); +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php new file mode 100644 index 000000000..d81abf404 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php @@ -0,0 +1,37 @@ + (microtime(true) - $start)) { + usleep(1000); +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php new file mode 100644 index 000000000..df48c4f55 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * @author Robert Schönthal + */ +class PhpExecutableFinderTest extends \PHPUnit_Framework_TestCase +{ + /** + * tests find() with the env var PHP_PATH + */ + public function testFindWithPhpPath() + { + if (defined('PHP_BINARY')) { + $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4'); + } + + $f = new PhpExecutableFinder(); + + $current = $f->find(); + + //not executable PHP_PATH + putenv('PHP_PATH=/not/executable/php'); + $this->assertFalse($f->find(), '::find() returns false for not executable PHP'); + + //executable PHP_PATH + putenv('PHP_PATH='.$current); + $this->assertEquals($f->find(), $current, '::find() returns the executable PHP'); + } + + /** + * tests find() with default executable + */ + public function testFindWithSuffix() + { + if (defined('PHP_BINARY')) { + $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4'); + } + + putenv('PHP_PATH='); + putenv('PHP_PEAR_PHP_BIN='); + $f = new PhpExecutableFinder(); + + $current = $f->find(); + + //TODO maybe php executable is custom or even Windows + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertTrue(is_executable($current)); + $this->assertTrue((bool) preg_match('/'.addSlashes(DIRECTORY_SEPARATOR).'php\.(exe|bat|cmd|com)$/i', $current), '::find() returns the executable PHP with suffixes'); + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php new file mode 100644 index 000000000..df66ad624 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\PhpProcess; + +class PhpProcessTest extends \PHPUnit_Framework_TestCase +{ + public function testNonBlockingWorks() + { + $expected = 'hello world!'; + $process = new PhpProcess(<<start(); + $process->wait(); + $this->assertEquals($expected, $process->getOutput()); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php new file mode 100644 index 000000000..cdc75255e --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php @@ -0,0 +1,63 @@ + 0) { + $written = fwrite(STDOUT, (binary) $out, 32768); + if (false === $written) { + die(ERR_WRITE_FAILED); + } + $out = (binary) substr($out, $written); + } + if (null === $read && strlen($out) < 1) { + $write = array_diff($write, array(STDOUT)); + } + + if (in_array(STDERR, $w) && strlen($err) > 0) { + $written = fwrite(STDERR, (binary) $err, 32768); + if (false === $written) { + die(ERR_WRITE_FAILED); + } + $err = (binary) substr($err, $written); + } + if (null === $read && strlen($err) < 1) { + $write = array_diff($write, array(STDERR)); + } + + if ($r) { + $str = fread(STDIN, 32768); + if (false !== $str) { + $out .= $str; + $err .= $str; + } + if (false === $str || feof(STDIN)) { + $read = null; + if (!feof(STDIN)) { + die(ERR_READ_FAILED); + } + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php new file mode 100644 index 000000000..ee14fa9cb --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\ProcessBuilder; + +class ProcessBuilderTest extends \PHPUnit_Framework_TestCase +{ + public function testInheritEnvironmentVars() + { + $_ENV['MY_VAR_1'] = 'foo'; + + $proc = ProcessBuilder::create() + ->add('foo') + ->getProcess(); + + unset($_ENV['MY_VAR_1']); + + $env = $proc->getEnv(); + $this->assertArrayHasKey('MY_VAR_1', $env); + $this->assertEquals('foo', $env['MY_VAR_1']); + } + + public function testAddEnvironmentVariables() + { + $pb = new ProcessBuilder(); + $env = array( + 'foo' => 'bar', + 'foo2' => 'bar2', + ); + $proc = $pb + ->add('command') + ->setEnv('foo', 'bar2') + ->addEnvironmentVariables($env) + ->inheritEnvironmentVariables(false) + ->getProcess() + ; + + $this->assertSame($env, $proc->getEnv()); + } + + public function testProcessShouldInheritAndOverrideEnvironmentVars() + { + $_ENV['MY_VAR_1'] = 'foo'; + + $proc = ProcessBuilder::create() + ->setEnv('MY_VAR_1', 'bar') + ->add('foo') + ->getProcess(); + + unset($_ENV['MY_VAR_1']); + + $env = $proc->getEnv(); + $this->assertArrayHasKey('MY_VAR_1', $env); + $this->assertEquals('bar', $env['MY_VAR_1']); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException + */ + public function testNegativeTimeoutFromSetter() + { + $pb = new ProcessBuilder(); + $pb->setTimeout(-1); + } + + public function testNullTimeout() + { + $pb = new ProcessBuilder(); + $pb->setTimeout(10); + $pb->setTimeout(null); + + $r = new \ReflectionObject($pb); + $p = $r->getProperty('timeout'); + $p->setAccessible(true); + + $this->assertNull($p->getValue($pb)); + } + + public function testShouldSetArguments() + { + $pb = new ProcessBuilder(array('initial')); + $pb->setArguments(array('second')); + + $proc = $pb->getProcess(); + + $this->assertContains("second", $proc->getCommandLine()); + } + + public function testPrefixIsPrependedToAllGeneratedProcess() + { + $pb = new ProcessBuilder(); + $pb->setPrefix('/usr/bin/php'); + + $proc = $pb->setArguments(array('-v'))->getProcess(); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php" "-v"', $proc->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php' '-v'", $proc->getCommandLine()); + } + + $proc = $pb->setArguments(array('-i'))->getProcess(); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php" "-i"', $proc->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php' '-i'", $proc->getCommandLine()); + } + } + + public function testArrayPrefixesArePrependedToAllGeneratedProcess() + { + $pb = new ProcessBuilder(); + $pb->setPrefix(array('/usr/bin/php', 'composer.phar')); + + $proc = $pb->setArguments(array('-v'))->getProcess(); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php" "composer.phar" "-v"', $proc->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php' 'composer.phar' '-v'", $proc->getCommandLine()); + } + + $proc = $pb->setArguments(array('-i'))->getProcess(); + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php" "composer.phar" "-i"', $proc->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php' 'composer.phar' '-i'", $proc->getCommandLine()); + } + } + + public function testShouldEscapeArguments() + { + $pb = new ProcessBuilder(array('%path%', 'foo " bar', '%baz%baz')); + $proc = $pb->getProcess(); + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertSame('^%"path"^% "foo \\" bar" "%baz%baz"', $proc->getCommandLine()); + } else { + $this->assertSame("'%path%' 'foo \" bar' '%baz%baz'", $proc->getCommandLine()); + } + } + + public function testShouldEscapeArgumentsAndPrefix() + { + $pb = new ProcessBuilder(array('arg')); + $pb->setPrefix('%prefix%'); + $proc = $pb->getProcess(); + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertSame('^%"prefix"^% "arg"', $proc->getCommandLine()); + } else { + $this->assertSame("'%prefix%' 'arg'", $proc->getCommandLine()); + } + } + + /** + * @expectedException \Symfony\Component\Process\Exception\LogicException + */ + public function testShouldThrowALogicExceptionIfNoPrefixAndNoArgument() + { + ProcessBuilder::create()->getProcess(); + } + + public function testShouldNotThrowALogicExceptionIfNoArgument() + { + $process = ProcessBuilder::create() + ->setPrefix('/usr/bin/php') + ->getProcess(); + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); + } + } + + public function testShouldNotThrowALogicExceptionIfNoPrefix() + { + $process = ProcessBuilder::create(array('/usr/bin/php')) + ->getProcess(); + + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); + } else { + $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php new file mode 100644 index 000000000..5da55e75e --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\Exception\ProcessFailedException; + +/** + * @author Sebastian Marek + */ +class ProcessFailedExceptionTest extends \PHPUnit_Framework_TestCase +{ + /** + * tests ProcessFailedException throws exception if the process was successful + */ + public function testProcessFailedExceptionThrowsException() + { + $process = $this->getMock( + 'Symfony\Component\Process\Process', + array('isSuccessful'), + array('php') + ); + $process->expects($this->once()) + ->method('isSuccessful') + ->will($this->returnValue(true)); + + $this->setExpectedException( + '\InvalidArgumentException', + 'Expected a failed process, but the given process was successful.' + ); + + new ProcessFailedException($process); + } + + /** + * tests ProcessFailedException uses information from process output + * to generate exception message + */ + public function testProcessFailedExceptionPopulatesInformationFromProcessOutput() + { + $cmd = 'php'; + $exitCode = 1; + $exitText = 'General error'; + $output = "Command output"; + $errorOutput = "FATAL: Unexpected error"; + + $process = $this->getMock( + 'Symfony\Component\Process\Process', + array('isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText'), + array($cmd) + ); + $process->expects($this->once()) + ->method('isSuccessful') + ->will($this->returnValue(false)); + $process->expects($this->once()) + ->method('getOutput') + ->will($this->returnValue($output)); + $process->expects($this->once()) + ->method('getErrorOutput') + ->will($this->returnValue($errorOutput)); + $process->expects($this->once()) + ->method('getExitCode') + ->will($this->returnValue($exitCode)); + $process->expects($this->once()) + ->method('getExitCodeText') + ->will($this->returnValue($exitText)); + + $exception = new ProcessFailedException($process); + + $this->assertEquals( + "The command \"$cmd\" failed.\nExit Code: $exitCode($exitText)\n\nOutput:\n================\n{$output}\n\nError Output:\n================\n{$errorOutput}", + $exception->getMessage() + ); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php new file mode 100644 index 000000000..3977bcdcf --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\Process; + +class ProcessInSigchildEnvironment extends Process +{ + protected function isSigchildEnabled() + { + return true; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php new file mode 100644 index 000000000..8ba94c114 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\ProcessUtils; + +class ProcessUtilsTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider dataArguments + */ + public function testEscapeArgument($result, $argument) + { + $this->assertSame($result, ProcessUtils::escapeArgument($argument)); + } + + public function dataArguments() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + return array( + array('"\"php\" \"-v\""', '"php" "-v"'), + array('"foo bar"', 'foo bar'), + array('^%"path"^%', '%path%'), + array('"<|>\\" \\"\'f"', '<|>" "\'f'), + array('""', ''), + array('"with\trailingbs\\\\"', 'with\trailingbs\\'), + ); + } + + return array( + array("'\"php\" \"-v\"'", '"php" "-v"'), + array("'foo bar'", 'foo bar'), + array("'%path%'", '%path%'), + array("'<|>\" \"'\\''f'", '<|>" "\'f'), + array("''", ''), + array("'with\\trailingbs\\'", 'with\trailingbs\\'), + ); + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php new file mode 100644 index 000000000..798e66a57 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +class SigchildDisabledProcessTest extends AbstractProcessTest +{ + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testGetExitCode() + { + parent::testGetExitCode(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testGetExitCodeIsNullOnStart() + { + parent::testGetExitCodeIsNullOnStart(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testGetExitCodeIsNullOnWhenStartingAgain() + { + parent::testGetExitCodeIsNullOnWhenStartingAgain(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testExitCodeCommandFailed() + { + parent::testExitCodeCommandFailed(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessIsSignaledIfStopped() + { + parent::testProcessIsSignaledIfStopped(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithTermSignal() + { + parent::testProcessWithTermSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessIsNotSignaled() + { + parent::testProcessIsNotSignaled(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithoutTermSignal() + { + parent::testProcessWithoutTermSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testCheckTimeoutOnStartedProcess() + { + parent::testCheckTimeoutOnStartedProcess(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPid() + { + parent::testGetPid(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPidIsNullBeforeStart() + { + parent::testGetPidIsNullBeforeStart(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPidIsNullAfterRun() + { + parent::testGetPidIsNullAfterRun(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testExitCodeText() + { + $process = $this->getProcess('qdfsmfkqsdfmqmsd'); + $process->run(); + + $process->getExitCodeText(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testExitCodeTextIsNullWhenExitCodeIsNull() + { + parent::testExitCodeTextIsNullWhenExitCodeIsNull(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testIsSuccessful() + { + parent::testIsSuccessful(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testIsSuccessfulOnlyAfterTerminated() + { + parent::testIsSuccessfulOnlyAfterTerminated(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testIsNotSuccessful() + { + parent::testIsNotSuccessful(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. + */ + public function testTTYCommandExitCode() + { + parent::testTTYCommandExitCode(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled. + */ + public function testSignal() + { + parent::testSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithoutTermSignalIsNotSignaled() + { + parent::testProcessWithoutTermSignalIsNotSignaled(); + } + + public function testStopWithTimeoutIsActuallyWorking() + { + $this->markTestSkipped('Stopping with signal is not supported in sigchild environment'); + } + + public function testProcessThrowsExceptionWhenExternallySignaled() + { + $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); + } + + public function testExitCodeIsAvailableAfterSignal() + { + $this->markTestSkipped('Signal is not supported in sigchild environment'); + } + + /** + * {@inheritdoc} + */ + protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) + { + $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options); + $process->setEnhanceSigchildCompatibility(false); + + return $process; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php new file mode 100644 index 000000000..65dd4bb57 --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php @@ -0,0 +1,133 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +class SigchildEnabledProcessTest extends AbstractProcessTest +{ + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessIsSignaledIfStopped() + { + parent::testProcessIsSignaledIfStopped(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithTermSignal() + { + parent::testProcessWithTermSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessIsNotSignaled() + { + parent::testProcessIsNotSignaled(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithoutTermSignal() + { + parent::testProcessWithoutTermSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPid() + { + parent::testGetPid(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPidIsNullBeforeStart() + { + parent::testGetPidIsNullBeforeStart(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. + */ + public function testGetPidIsNullAfterRun() + { + parent::testGetPidIsNullAfterRun(); + } + + public function testExitCodeText() + { + $process = $this->getProcess('qdfsmfkqsdfmqmsd'); + $process->run(); + + $this->assertInternalType('string', $process->getExitCodeText()); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled. + */ + public function testSignal() + { + parent::testSignal(); + } + + /** + * @expectedException \Symfony\Component\Process\Exception\RuntimeException + * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. + */ + public function testProcessWithoutTermSignalIsNotSignaled() + { + parent::testProcessWithoutTermSignalIsNotSignaled(); + } + + public function testProcessThrowsExceptionWhenExternallySignaled() + { + $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); + } + + public function testExitCodeIsAvailableAfterSignal() + { + $this->markTestSkipped('Signal is not supported in sigchild environment'); + } + + public function testStartAfterATimeout() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Restarting a timed-out process on Windows is not supported in sigchild environment'); + } + parent::testStartAfterATimeout(); + } + + /** + * {@inheritdoc} + */ + protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) + { + $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options); + $process->setEnhanceSigchildCompatibility(true); + + return $process; + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php new file mode 100644 index 000000000..143515d4d --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Process\Tests; + +use Symfony\Component\Process\Process; + +class SimpleProcessTest extends AbstractProcessTest +{ + private $enabledSigchild = false; + + public function setUp() + { + ob_start(); + phpinfo(INFO_GENERAL); + + $this->enabledSigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); + } + + public function testGetExitCode() + { + $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case + parent::testGetExitCode(); + } + + public function testExitCodeCommandFailed() + { + $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case + parent::testExitCodeCommandFailed(); + } + + public function testProcessIsSignaledIfStopped() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); + parent::testProcessIsSignaledIfStopped(); + } + + public function testProcessWithTermSignal() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); + parent::testProcessWithTermSignal(); + } + + public function testProcessIsNotSignaled() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); + parent::testProcessIsNotSignaled(); + } + + public function testProcessWithoutTermSignal() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); + parent::testProcessWithoutTermSignal(); + } + + public function testExitCodeText() + { + $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case + parent::testExitCodeText(); + } + + public function testIsSuccessful() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testIsSuccessful(); + } + + public function testIsNotSuccessful() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testIsNotSuccessful(); + } + + public function testGetPid() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testGetPid(); + } + + public function testGetPidIsNullBeforeStart() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testGetPidIsNullBeforeStart(); + } + + public function testGetPidIsNullAfterRun() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testGetPidIsNullAfterRun(); + } + + public function testSignal() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + parent::testSignal(); + } + + public function testProcessWithoutTermSignalIsNotSignaled() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); + parent::testProcessWithoutTermSignalIsNotSignaled(); + } + + public function testProcessThrowsExceptionWhenExternallySignaled() + { + $this->skipIfPHPSigchild(); // This test use PID that is not available in this case + parent::testProcessThrowsExceptionWhenExternallySignaled(); + } + + public function testExitCodeIsAvailableAfterSignal() + { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + parent::testExitCodeIsAvailableAfterSignal(); + } + + public function testSignalProcessNotRunning() + { + $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Can not send signal on a non running process.'); + parent::testSignalProcessNotRunning(); + } + + public function testSignalWithWrongIntSignal() + { + if ($this->enabledSigchild) { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + } else { + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `-4`.'); + } + parent::testSignalWithWrongIntSignal(); + } + + public function testSignalWithWrongNonIntSignal() + { + if ($this->enabledSigchild) { + $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); + } else { + $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `Céphalopodes`.'); + } + parent::testSignalWithWrongNonIntSignal(); + } + + /** + * {@inheritdoc} + */ + protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) + { + return new Process($commandline, $cwd, $env, $stdin, $timeout, $options); + } + + private function skipIfPHPSigchild() + { + if ($this->enabledSigchild) { + $this->markTestSkipped('Your PHP has been compiled with --enable-sigchild, this test can not be executed'); + } + } + + private function expectExceptionIfPHPSigchild($classname, $message) + { + if ($this->enabledSigchild) { + $this->setExpectedException($classname, $message); + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/composer.json b/core/lib/symfony/process/Symfony/Component/Process/composer.json new file mode 100644 index 000000000..a05735b8c --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/process", + "type": "library", + "description": "Symfony Process Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Process\\": "" } + }, + "target-dir": "Symfony/Component/Process", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist b/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist new file mode 100644 index 000000000..9d5830f9e --- /dev/null +++ b/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + + + + From 01c1470f695e268435ed42e79aaeb1de221da99e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:18:22 -0400 Subject: [PATCH 054/166] composer generated files --- composer.json | 13 + composer.lock | 449 ++++++++++++++++++++++ core/lib/autoload.php | 7 + core/lib/composer/ClassLoader.php | 364 ++++++++++++++++++ core/lib/composer/autoload_classmap.php | 9 + core/lib/composer/autoload_namespaces.php | 18 + core/lib/composer/autoload_psr4.php | 9 + core/lib/composer/autoload_real.php | 53 +++ core/lib/composer/installed.json | 440 +++++++++++++++++++++ 9 files changed, 1362 insertions(+) create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 core/lib/autoload.php create mode 100644 core/lib/composer/ClassLoader.php create mode 100644 core/lib/composer/autoload_classmap.php create mode 100644 core/lib/composer/autoload_namespaces.php create mode 100644 core/lib/composer/autoload_psr4.php create mode 100644 core/lib/composer/autoload_real.php create mode 100644 core/lib/composer/installed.json diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..1c21acf24 --- /dev/null +++ b/composer.json @@ -0,0 +1,13 @@ +{ + "config": { + "vendor-dir": "core/lib" + }, + "autoload": { + "psr-0": { + "": "core/lib/" + } + }, + "require": { + "alchemy/zippy": "0.2.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 000000000..35339bfe5 --- /dev/null +++ b/composer.lock @@ -0,0 +1,449 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "847848f40fffd1dc29a257597bdc2576", + "packages": [ + { + "name": "alchemy/zippy", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/alchemy-fr/Zippy.git", + "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/alchemy-fr/Zippy/zipball/e652161e1d99f647b34c3ff9695bb4bb0b8837cf", + "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf", + "shasum": "" + }, + "require": { + "doctrine/collections": "~1.0", + "guzzle/guzzle": "~3.0", + "php": ">=5.3.3", + "pimple/pimple": "~1.0", + "symfony/filesystem": "~2.0", + "symfony/process": "~2.0" + }, + "require-dev": { + "ext-zip": "*", + "phpunit/phpunit": "~3.7", + "sami/sami": "dev-master@dev", + "symfony/finder": "~2.0" + }, + "suggest": { + "ext-zip": "To use the ZipExtensionAdapter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Alchemy": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alchemy", + "email": "dev.team@alchemy.fr", + "homepage": "http://www.alchemy.fr/" + } + ], + "description": "Zippy, the archive manager companion", + "keywords": [ + "bzip", + "compression", + "tar", + "zip" + ], + "time": "2014-04-04 16:24:46" + }, + { + "name": "doctrine/collections", + "version": "v1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", + "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": "Creator" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2014-02-03 23:07:43" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "92d9934f2fca1da15178c91239576ae26e505e60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60", + "reference": "92d9934f2fca1da15178c91239576ae26e505e60", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.8-dev" + } + }, + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "time": "2014-05-07 17:04:22" + }, + { + "name": "pimple/pimple", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Pimple.git", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ], + "time": "2013-11-22 08:30:29" + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.4.4", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "e539602e5455aa086c0e81e604745af7789e4d8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a", + "reference": "e539602e5455aa086c0e81e604745af7789e4d8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com", + "time": "2014-04-16 10:34:31" + }, + { + "name": "symfony/filesystem", + "version": "v2.4.4", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4", + "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2014-04-16 10:34:31" + }, + { + "name": "symfony/process", + "version": "v2.4.4", + "target-dir": "Symfony/Component/Process", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", + "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "http://symfony.com", + "time": "2014-04-27 13:34:57" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ], + "platform": [ + + ], + "platform-dev": [ + + ] +} diff --git a/core/lib/autoload.php b/core/lib/autoload.php new file mode 100644 index 000000000..e972d4b14 --- /dev/null +++ b/core/lib/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + public function getPrefixes() + { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + */ + public function setPsr4($prefix, $paths) { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/core/lib/composer/autoload_classmap.php b/core/lib/composer/autoload_classmap.php new file mode 100644 index 000000000..71dd9c179 --- /dev/null +++ b/core/lib/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + array($vendorDir . '/symfony/process'), + 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Pimple' => array($vendorDir . '/pimple/pimple/lib'), + 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), + 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), + 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), + 'Alchemy' => array($vendorDir . '/alchemy/zippy/src'), + '' => array($vendorDir), +); diff --git a/core/lib/composer/autoload_psr4.php b/core/lib/composer/autoload_psr4.php new file mode 100644 index 000000000..80607ee97 --- /dev/null +++ b/core/lib/composer/autoload_psr4.php @@ -0,0 +1,9 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + return $loader; + } +} + +function composerRequire140ff88b0c869590677b7653c76d6c6d($file) +{ + require $file; +} diff --git a/core/lib/composer/installed.json b/core/lib/composer/installed.json new file mode 100644 index 000000000..ef961958c --- /dev/null +++ b/core/lib/composer/installed.json @@ -0,0 +1,440 @@ +[ + { + "name": "symfony/filesystem", + "version": "v2.4.4", + "version_normalized": "2.4.4.0", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4", + "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2014-04-16 10:34:31", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com" + }, + { + "name": "symfony/process", + "version": "v2.4.4", + "version_normalized": "2.4.4.0", + "target-dir": "Symfony/Component/Process", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", + "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2014-04-27 13:34:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "http://symfony.com" + }, + { + "name": "pimple/pimple", + "version": "v1.1.1", + "version_normalized": "1.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/fabpot/Pimple.git", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fabpot/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2013-11-22 08:30:29", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pimple": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", + "homepage": "http://pimple.sensiolabs.org", + "keywords": [ + "container", + "dependency injection" + ] + }, + { + "name": "symfony/event-dispatcher", + "version": "v2.4.4", + "version_normalized": "2.4.4.0", + "target-dir": "Symfony/Component/EventDispatcher", + "source": { + "type": "git", + "url": "https://github.com/symfony/EventDispatcher.git", + "reference": "e539602e5455aa086c0e81e604745af7789e4d8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a", + "reference": "e539602e5455aa086c0e81e604745af7789e4d8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~2.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2014-04-16 10:34:31", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "http://symfony.com" + }, + { + "name": "guzzle/guzzle", + "version": "v3.9.1", + "version_normalized": "3.9.1.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "92d9934f2fca1da15178c91239576ae26e505e60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60", + "reference": "92d9934f2fca1da15178c91239576ae26e505e60", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": "~2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" + }, + "require-dev": { + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", + "phpunit/phpunit": "3.7.*", + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "time": "2014-05-07 17:04:22", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ] + }, + { + "name": "doctrine/collections", + "version": "v1.2", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", + "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "time": "2014-02-03 23:07:43", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com", + "homepage": "http://www.jwage.com/", + "role": "Creator" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com", + "homepage": "http://www.instaclick.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "http://jmsyst.com", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ] + }, + { + "name": "alchemy/zippy", + "version": "0.2.0", + "version_normalized": "0.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/alchemy-fr/Zippy.git", + "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/alchemy-fr/Zippy/zipball/e652161e1d99f647b34c3ff9695bb4bb0b8837cf", + "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf", + "shasum": "" + }, + "require": { + "doctrine/collections": "~1.0", + "guzzle/guzzle": "~3.0", + "php": ">=5.3.3", + "pimple/pimple": "~1.0", + "symfony/filesystem": "~2.0", + "symfony/process": "~2.0" + }, + "require-dev": { + "ext-zip": "*", + "phpunit/phpunit": "~3.7", + "sami/sami": "dev-master@dev", + "symfony/finder": "~2.0" + }, + "suggest": { + "ext-zip": "To use the ZipExtensionAdapter" + }, + "time": "2014-04-04 16:24:46", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.2.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Alchemy": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alchemy", + "email": "dev.team@alchemy.fr", + "homepage": "http://www.alchemy.fr/" + } + ], + "description": "Zippy, the archive manager companion", + "keywords": [ + "bzip", + "compression", + "tar", + "zip" + ] + } +] From 77dc022702c68617b5fa72ee8d5dd5777a140a0f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:18:49 -0400 Subject: [PATCH 055/166] separate functions to return values rather than booleans --- core/lib/PatternLab/Console.php | 42 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index 3a39a61f5..f5f793a43 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -40,7 +40,7 @@ public function getArguments() { } /** - * See if a particular command was passed to the script via the command line. Can either be the short or long version + * See if a particular command was passed to the script via the command line and return a boolean. Can either be the short or long version * @param {String} list of arguments to check * * @return {Boolean} if the command has been passed to the script via the command line @@ -49,7 +49,23 @@ public function findCommand($args) { $args = explode("|",$args); foreach ($args as $arg) { if (isset($this->options[$arg])) { - return true; + return empty($this->options[$arg]) ? true : $this->options[$arg]; + } + } + return false; + } + + /** + * See if a particular command was passed to the script via the command line and return a value. Can either be the short or long version + * @param {String} list of arguments to check + * + * @return {String} the value that was passed via the command line + */ + public function findCommandValue($args) { + $args = explode("|",$args); + foreach ($args as $arg) { + if (isset($this->options[$arg])) { + return $this->options[$arg]; } } return false; @@ -79,11 +95,11 @@ public function getCommand() { public function setCommand($short,$long,$desc,$help) { $this->optionsShort .= $short; $this->optionsLong[] = $long; - $this->commands[$short] = array("commandShort" => $short, "commandLong" => $long, "commandLongLength" => strlen($long), "commandDesc" => $desc, "commandHelp" => $help, "commandOptions" => array()); + $this->commands[$short[0]] = array("commandShort" => $short, "commandLong" => $long, "commandLongLength" => strlen($long), "commandDesc" => $desc, "commandHelp" => $help, "commandOptions" => array()); } /** - * See if a particular option was passed to the script via the command line. Can either be the short or long version + * See if a particular option was passed to the script via the command line and return a boolean. Can either be the short or long version * @param {String} list of arguments to check * * @return {Boolean} if the command has been passed to the script via the command line @@ -92,7 +108,23 @@ public function findCommandOption($args) { $args = explode("|",$args); foreach ($args as $arg) { if (isset($this->options[$arg])) { - return empty($this->options[$arg]) ? true : $this->options[$arg]; + return true; + } + } + return false; + } + + /** + * See if a particular option was passed to the script via the command line and return a value. Can either be the short or long version + * @param {String} list of arguments to check + * + * @return {String} the value that was passed via the command line + */ + public function findCommandOptionValue($args) { + $args = explode("|",$args); + foreach ($args as $arg) { + if (isset($this->options[$arg])) { + return $this->options[$arg]; } } return false; From feb7f9d597f11b9e196fafb98304943acd7f5737 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:19:19 -0400 Subject: [PATCH 056/166] composer autoload support --- core/builder.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/core/builder.php b/core/builder.php index dc9482a26..b2376b461 100644 --- a/core/builder.php +++ b/core/builder.php @@ -19,17 +19,7 @@ } // auto-load classes -require(__DIR__."/lib/SplClassLoader.php"); - -$loader = new SplClassLoader('PatternLab', __DIR__.'/lib'); -$loader->register(); - -$loader = new SplClassLoader('Mustache', __DIR__.'/lib'); -$loader->setNamespaceSeparator("_"); -$loader->register(); - -$loader = new SplClassLoader('Seld\JsonLint', __DIR__.'/lib'); -$loader->register(); +require(__DIR__.'/lib/autoload.php'); /******************************* * Console Set-up From e523370ad38c04628f4ae2ed898ed424a098b958 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:19:45 -0400 Subject: [PATCH 057/166] separate function for returning the option value --- core/builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/builder.php b/core/builder.php index b2376b461..cbef60904 100644 --- a/core/builder.php +++ b/core/builder.php @@ -82,7 +82,6 @@ $moveStatic = ($console->findCommandOption("p|patternsonly")) ? false : true; $noCacheBuster = $console->findCommandOption("n|nocache"); $autoReload = $console->findCommandOption("r|autoreload"); - $snapshotDir = $console->findCommandOption("d|dir"); if (($command == "g") || ($command == "b")) { @@ -107,6 +106,7 @@ } else if ($command == "s") { // run the snapshot command + $snapshotDir = $console->findCommandOptionValue("d|dir"); $s = new PatternLab\Snapshot($config); $s->takeSnapshot($snapshotDir); From 0fa8da0bb817f2820388b5c445d9cddef40afa3c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:20:14 -0400 Subject: [PATCH 058/166] adding fetch command support --- core/builder.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/builder.php b/core/builder.php index cbef60904..6c0f0f394 100644 --- a/core/builder.php +++ b/core/builder.php @@ -47,8 +47,7 @@ $console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); // set-up the fetch command and options -$console->setCommand("s","snapshot","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); -$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); +$console->setCommand("f:","fetch:","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); @@ -56,7 +55,6 @@ // set-up the help command $console->setCommand("h","help","Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options."); - /******************************* * Figure out what to run *******************************/ @@ -110,6 +108,13 @@ $s = new PatternLab\Snapshot($config); $s->takeSnapshot($snapshotDir); + } else if ($command == "f") { + + // run the snapshot command + $starterKit = $console->findCommandValue("f|fetch"); + $sk = new PatternLab\StarterKit($config); + $sk->fetch($starterKit); + } else if ($command == "v") { // write out the version number From f4ac46312c2e262c43119ed2e1ff51fe2afb37dd Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:20:23 -0400 Subject: [PATCH 059/166] adding starter kit support --- core/lib/PatternLab/StarterKit.php | 119 ++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 21 deletions(-) diff --git a/core/lib/PatternLab/StarterKit.php b/core/lib/PatternLab/StarterKit.php index 95adbff7e..9d2bfa7c3 100644 --- a/core/lib/PatternLab/StarterKit.php +++ b/core/lib/PatternLab/StarterKit.php @@ -18,7 +18,11 @@ class StarterKit { * Set-up a default var */ public function __construct($config) { - $this->sourceDir = __DIR__."/../../../".$config["publicDir"]; + $this->sourceDir = __DIR__."/../../../".$config["sourceDir"]; + if (!is_dir($this->sourceDir)) { + print "Check to make sure your source directory is configured properly...\n"; + exit; + } } /** @@ -27,41 +31,114 @@ public function __construct($config) { * * @return {String} the modified file contents */ - public function fetch($org,$repo,$tag) { + public function fetch($starterKit) { + + // see if the user passed anythign useful + if (empty($starterKit)) { + print "please provide a path for the starter kit before trying to fetch it...\n"; + exit; + } - //master - //tag + // figure out the options for the GH path + list($org,$repo,$tag) = $this->getStarterKitInfo($starterKit); //get the path to the GH repo and validate it - $tarballUrl = "https://github.com/".$org."/".$repo."/archive/".$tag.".tar.gzz"; + $tarballUrl = "https://github.com/".$org."/".$repo."/archive/".$tag.".tar.gz"; + + print "downloading the starter kit...\n"; // try to download the given starter kit - try { - $starterKit = file_get_contents($tarballUrl); - } catch (\Exception $e) { - throw new \Exception('Something really gone wrong'.$e->getMessage(), 0, $e); + if (!$starterKit = @file_get_contents($tarballUrl)) { + $error = error_get_last(); + print "starter kit wasn't downloaded because:\n ".$error["message"]."\n"; + exit; } // write the starter kit to the temp directory $tempFile = tempnam(sys_get_temp_dir(), "pl-sk-archive.tar.gz"); file_put_contents($tempFile, $starterKit); - $zip = new ZipArchive; - if ($zip->open('test.zip') === TRUE) { - $zip->extractTo('/my/destination/dir/'); - $zip->close(); - echo 'ok'; - } else { - echo 'failed'; + // see if the source directory is empty + $emptyDir = true; + $objects = new \DirectoryIterator($this->sourceDir); + foreach ($objects as $object) { + if (!$object->isDot() && ($object->getFilename() != "README") && ($object->getFilename() != ".DS_Store")) { + $emptyDir = false; + } + } + + print "installing the starter kit...\n"; + + // if source directory isn't empty ask if it's ok to nuke what's there + if (!$emptyDir) { + $stdin = fopen("php://stdin", "r"); + print("delete everything in source/ before installing the starter kit? Y/n\n"); + $answer = strtolower(trim(fgets($stdin))); + fclose($stdin); + if ($answer == "y") { + + print "nuking everything in source/...\n"; + + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sourceDir), \RecursiveIteratorIterator::CHILD_FIRST); + + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); + + foreach($objects as $name => $object) { + + if ($object->isDir()) { + rmdir($name); + } else if ($object->isFile()) { + unlink($name); + } + + } + + } else { + print "aborting install of the starter kit...\n"; + unlink($tempFile); + exit; + } + } + // extract + $zippy = \Alchemy\Zippy\Zippy::load(); + $zipAdapter = $zippy->getAdapterFor('tar.gz'); + $archiveZip = $zipAdapter->open($tempFile); + $archiveZip = $archiveZip->extract($this->sourceDir); + + // remove the temp file unlink($tempFile); - //see if source is empty, if not prompt if they want to delete stuff - //delete everything - //download - //unzip - //unpack into source + print "starter kit installation complete...\n"; + + } + + /** + * Break up the starterKit path + * @param {String} path of the GitHub repo + * + * @return {Array} the parts of the starter kit path + */ + protected function getStarterKitInfo($starterKit) { + + $org = ""; + $repo = ""; + $tag = "master"; + + if (strpos($starterKit, "#") !== false) { + list($starterKit,$tag) = explode("#",$starterKit); + } + + if (strpos($starterKit, "/") !== false) { + list($org,$repo) = explode("/",$starterKit); + } else { + print "please provide a real path to a starter kit...\n"; + exit; + } + + return array($org,$repo,$tag); } From 44a7de4a6b4a0099c58f6f017b428af1a4ce3bbf Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 16:21:12 -0400 Subject: [PATCH 060/166] removing old auto-loader --- core/lib/SplClassLoader.php | 136 ------------------------------------ 1 file changed, 136 deletions(-) delete mode 100644 core/lib/SplClassLoader.php diff --git a/core/lib/SplClassLoader.php b/core/lib/SplClassLoader.php deleted file mode 100644 index 2bc31f04b..000000000 --- a/core/lib/SplClassLoader.php +++ /dev/null @@ -1,136 +0,0 @@ -register(); - * - * @author Jonathan H. Wage - * @author Roman S. Borschel - * @author Matthew Weier O'Phinney - * @author Kris Wallsmith - * @author Fabien Potencier - */ -class SplClassLoader -{ - private $_fileExtension = '.php'; - private $_namespace; - private $_includePath; - private $_namespaceSeparator = '\\'; - - /** - * Creates a new SplClassLoader that loads classes of the - * specified namespace. - * - * @param string $ns The namespace to use. - */ - public function __construct($ns = null, $includePath = null) - { - $this->_namespace = $ns; - $this->_includePath = $includePath; - } - - /** - * Sets the namespace separator used by classes in the namespace of this class loader. - * - * @param string $sep The separator to use. - */ - public function setNamespaceSeparator($sep) - { - $this->_namespaceSeparator = $sep; - } - - /** - * Gets the namespace seperator used by classes in the namespace of this class loader. - * - * @return void - */ - public function getNamespaceSeparator() - { - return $this->_namespaceSeparator; - } - - /** - * Sets the base include path for all class files in the namespace of this class loader. - * - * @param string $includePath - */ - public function setIncludePath($includePath) - { - $this->_includePath = $includePath; - } - - /** - * Gets the base include path for all class files in the namespace of this class loader. - * - * @return string $includePath - */ - public function getIncludePath() - { - return $this->_includePath; - } - - /** - * Sets the file extension of class files in the namespace of this class loader. - * - * @param string $fileExtension - */ - public function setFileExtension($fileExtension) - { - $this->_fileExtension = $fileExtension; - } - - /** - * Gets the file extension of class files in the namespace of this class loader. - * - * @return string $fileExtension - */ - public function getFileExtension() - { - return $this->_fileExtension; - } - - /** - * Installs this class loader on the SPL autoload stack. - */ - public function register() - { - spl_autoload_register(array($this, 'loadClass')); - } - - /** - * Uninstalls this class loader from the SPL autoloader stack. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $className The name of the class to load. - * @return void - */ - public function loadClass($className) - { - if (null === $this->_namespace || $this->_namespace.$this->_namespaceSeparator === substr($className, 0, strlen($this->_namespace.$this->_namespaceSeparator))) { - $fileName = ''; - $namespace = ''; - if (false !== ($lastNsPos = strripos($className, $this->_namespaceSeparator))) { - $namespace = substr($className, 0, $lastNsPos); - $className = substr($className, $lastNsPos + 1); - $fileName = str_replace($this->_namespaceSeparator, DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; - } - $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . $this->_fileExtension; - - require ($this->_includePath !== null ? $this->_includePath . DIRECTORY_SEPARATOR : '') . $fileName; - } - } -} \ No newline at end of file From 935a1eb8bb477e5f6677adb0689f746e5da99893 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 17:02:50 -0400 Subject: [PATCH 061/166] updated wording for the s and f commands --- core/builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/builder.php b/core/builder.php index 6c0f0f394..343426382 100644 --- a/core/builder.php +++ b/core/builder.php @@ -43,11 +43,11 @@ $console->setCommandOption("w","r","autoreload","Turn on the auto-reload service.","To turn on auto-reload:"); // set-up the snapshot command and options -$console->setCommand("s","snapshot","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); $console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); +$console->setCommand("s","snapshot","Take a snapshot of public/","The snapshot command copies the current state of public/ and puts it in snapshots/v*/."); // set-up the fetch command and options -$console->setCommand("f:","fetch:","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); +$console->setCommand("f:","fetch:","Fetch a starter kit","The fetch command grabs a starter kit from GitHub and puts it into source/."); // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); From 07d0e50fb3590ac33e995ebf2d742bf0fcdb23d2 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 9 May 2014 17:03:12 -0400 Subject: [PATCH 062/166] added support for additions to command options and command examples --- core/builder.php | 4 +++- core/lib/PatternLab/Console.php | 40 ++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/core/builder.php b/core/builder.php index 343426382..9ca2109f3 100644 --- a/core/builder.php +++ b/core/builder.php @@ -43,11 +43,13 @@ $console->setCommandOption("w","r","autoreload","Turn on the auto-reload service.","To turn on auto-reload:"); // set-up the snapshot command and options -$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:"); $console->setCommand("s","snapshot","Take a snapshot of public/","The snapshot command copies the current state of public/ and puts it in snapshots/v*/."); +$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:","example-path/"); // set-up the fetch command and options $console->setCommand("f:","fetch:","Fetch a starter kit","The fetch command grabs a starter kit from GitHub and puts it into source/."); +$console->setCommandExample("f","Install starter kit:","pattern-lab/starter-kit-demo"); +$console->setCommandExample("f","Install a tagged version of a starter kit:","pattern-lab/starter-kit-demo#v1.0.0"); // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index f5f793a43..8f8842a7f 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -95,7 +95,19 @@ public function getCommand() { public function setCommand($short,$long,$desc,$help) { $this->optionsShort .= $short; $this->optionsLong[] = $long; - $this->commands[$short[0]] = array("commandShort" => $short, "commandLong" => $long, "commandLongLength" => strlen($long), "commandDesc" => $desc, "commandHelp" => $help, "commandOptions" => array()); + $short = str_replace(":","",$short); + $long = str_replace(":","",$long); + $this->commands[$short] = array("commandShort" => $short, "commandLong" => $long, "commandLongLength" => strlen($long), "commandDesc" => $desc, "commandHelp" => $help, "commandOptions" => array(), "commandExamples" => array()); + } + + /** + * Set-up an option for a given command so it can be used from the command line + * @param {String} the single character of the command that this option is related to + * @param {String} the sample to be used in the "sample" section of writeHelpCommand() + * @param {String} the extra info to be used in the example command for the "sample" section of writeHelpCommand() + */ + public function setCommandExample($command,$sample,$extra) { + $this->commands[$command]["commandExamples"][] = array("exampleSample" => $sample, "exampleExtra" => $extra); } /** @@ -137,15 +149,18 @@ public function findCommandOptionValue($args) { * @param {String} the long version of the option * @param {String} the description to be used in the "available options" section of writeHelpCommand() * @param {String} the sample to be used in the "sample" section of writeHelpCommand() + * @param {String} the extra info to be used in the example command for the "sample" section of writeHelpCommand() */ - public function setCommandOption($command,$short,$long,$desc,$sample) { + public function setCommandOption($command,$short,$long,$desc,$sample,$extra = "") { if (strpos($this->optionsShort,$short) === false) { $this->optionsShort .= $short; } if (!in_array($long,$this->optionsLong)) { $this->optionsLong[] = $long; } - $this->commands[$command]["commandOptions"][$short] = array("optionShort" => $short, "optionLong" => $long, "optionLongLength" => strlen($long), "optionDesc" => $desc, "optionSample" => $sample); + $short = str_replace(":","",$short); + $long = str_replace(":","",$long); + $this->commands[$command]["commandOptions"][$short] = array("optionShort" => $short, "optionLong" => $long, "optionLongLength" => strlen($long), "optionDesc" => $desc, "optionSample" => $sample, "optionExtra" => $extra); } /** @@ -237,7 +252,9 @@ public function writeHelpCommand($command = "") { $commandShort = $this->commands[$command]["commandShort"]; $commandLong = $this->commands[$command]["commandLong"]; $commandHelp = $this->commands[$command]["commandHelp"]; + $commandExtra = isset($this->commands[$command]["commandExtra"]) ? $this->commands[$command]["commandExtra"] : ""; $commandOptions = $this->commands[$command]["commandOptions"]; + $commandExamples = $this->commands[$command]["commandExamples"]; $commandLongUC = ucfirst($commandLong); @@ -268,12 +285,23 @@ public function writeHelpCommand($command = "") { $this->writeLine(" ".$commandHelp,true); // write out the samples - if (count($commandOptions) > 0) { + if ((count($commandOptions) > 0) || (count($commandExamples) > 0)) { $this->writeLine(" Samples:",true); + } + + if (count($commandExamples) > 0) { + foreach ($commandExamples as $example => $attributes) { + $this->writeLine(" ".$attributes["exampleSample"]); + $this->writeLine(" php ".$this->self." --".$commandLong." ".$attributes["exampleExtra"]); + $this->writeLine(" php ".$this->self." -".$commandShort." ".$attributes["exampleExtra"],true); + } + } + + if (count($commandOptions) > 0) { foreach ($commandOptions as $option => $attributes) { $this->writeLine(" ".$attributes["optionSample"]); - $this->writeLine(" php ".$this->self." --".$commandLong." --".$attributes["optionLong"]); - $this->writeLine(" php ".$this->self." -".$commandShort." -".$attributes["optionShort"],true); + $this->writeLine(" php ".$this->self." --".$commandLong." --".$attributes["optionLong"]." ".$attributes["optionExtra"]); + $this->writeLine(" php ".$this->self." -".$commandShort." -".$attributes["optionShort"]." ".$attributes["optionExtra"],true); } } From 1002f583c23f1af9f96cbe932f7801c80643a44d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 11:41:39 -0400 Subject: [PATCH 063/166] making the style guide check less explicit --- core/lib/PatternLab/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index df0daf333..81300b109 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -188,7 +188,7 @@ protected function generateMainPages() { $styleGuideFoot = $this->mv->render($this->mainPageFoot,$sd); $styleGuidePage = $styleGuideHead.$this->mfs->render('viewall',$sd).$styleGuideFoot; - if (!file_exists($this->pd."/styleguide/html/styleguide.html")) { + if (!is_dir($this->pd."/styleguide/html/")) { print "ERROR: the main style guide wasn't written out. make sure public/styleguide exists. can copy core/styleguide\n"; } else { file_put_contents($this->pd."/styleguide/html/styleguide.html",$styleGuidePage); From d58e2fb2058ec4996d147696c33f843244328a9e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 12:04:37 -0400 Subject: [PATCH 064/166] findCommand should return a boolean --- core/lib/PatternLab/Console.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index 8f8842a7f..e5500f107 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -49,7 +49,8 @@ public function findCommand($args) { $args = explode("|",$args); foreach ($args as $arg) { if (isset($this->options[$arg])) { - return empty($this->options[$arg]) ? true : $this->options[$arg]; + return true; + } } return false; From 6fe633d9432eabb834c5c655523bf64f74c80e7b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 12:05:00 -0400 Subject: [PATCH 065/166] renamed setCommandExample to setCommandSample --- core/lib/PatternLab/Console.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index e5500f107..689f270d5 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -102,12 +102,12 @@ public function setCommand($short,$long,$desc,$help) { } /** - * Set-up an option for a given command so it can be used from the command line + * Set a sample for a specific command * @param {String} the single character of the command that this option is related to * @param {String} the sample to be used in the "sample" section of writeHelpCommand() * @param {String} the extra info to be used in the example command for the "sample" section of writeHelpCommand() */ - public function setCommandExample($command,$sample,$extra) { + public function setCommandSample($command,$sample,$extra) { $this->commands[$command]["commandExamples"][] = array("exampleSample" => $sample, "exampleExtra" => $extra); } From cc02c8196ecabac7e9a5f859be8b62a23bcf0a4a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 12:05:19 -0400 Subject: [PATCH 066/166] added a method to return the short command when given a long command --- core/lib/PatternLab/Console.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index 689f270d5..f75739585 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -72,6 +72,21 @@ public function findCommandValue($args) { return false; } + /** + * Find the short command for a given long gommand + * @param {String} long command to search for + * + * @return {String} the search command + */ + public function findCommandShort($arg) { + foreach ($this->commands as $command => $commandOptions) { + if ($commandOptions["commandLong"] == $arg) { + return $command; + } + } + return false; + } + /** * Return the command that was given in the command line arguments * From 7d851c7cd8c488e7fe8142b152b288c29593890f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 12:05:47 -0400 Subject: [PATCH 067/166] tweaked the use of setCommandSample --- core/builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/builder.php b/core/builder.php index 9ca2109f3..cc9aa5b4e 100644 --- a/core/builder.php +++ b/core/builder.php @@ -48,8 +48,8 @@ // set-up the fetch command and options $console->setCommand("f:","fetch:","Fetch a starter kit","The fetch command grabs a starter kit from GitHub and puts it into source/."); -$console->setCommandExample("f","Install starter kit:","pattern-lab/starter-kit-demo"); -$console->setCommandExample("f","Install a tagged version of a starter kit:","pattern-lab/starter-kit-demo#v1.0.0"); +$console->setCommandSample("f","Install a starter kit:","github-org/github-repo"); +$console->setCommandSample("f","Install a tagged version of a starter kit:","github-org/github-repo#tag"); // set-up the version command $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); From 4424e44d966e6948a5eea6c91586a86ec7a7c666 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 12 May 2014 12:06:19 -0400 Subject: [PATCH 068/166] now use the command given after help to write out the appropriate help section --- core/builder.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/builder.php b/core/builder.php index cc9aa5b4e..9568e6062 100644 --- a/core/builder.php +++ b/core/builder.php @@ -55,7 +55,7 @@ $console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); // set-up the help command -$console->setCommand("h","help","Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options."); +$console->setCommand("h:","help:","Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options."); /******************************* * Figure out what to run @@ -64,10 +64,13 @@ // get what was passed on the command line $console->getArguments(); -if ($console->findCommand("h|help") && ($command = $console->getCommand())) { +if ($console->findCommand("h|help")) { - // write the usage & help for a specific command - $console->writeHelpCommand($command); + $helpCommand = $console->findCommandValue("h|help"); + $helpCommand = str_replace("-","",$helpCommand); + $helpCommand = (strlen($helpCommand) == 1) ? $helpCommand : $console->findCommandShort($helpCommand); + + $helpCommand ? $console->writeHelpCommand($helpCommand) : $console->writeHelp(); } else if ($command = $console->getCommand()) { From b7b63d2c2682ca57d996bda84750367d2ecf162b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:46:15 -0400 Subject: [PATCH 069/166] adding markdown lib --- core/lib/michelf/php-markdown/License.md | 36 + .../php-markdown/Michelf/Markdown.inc.php | 10 + .../michelf/php-markdown/Michelf/Markdown.php | 3106 +++++++++++++++++ .../Michelf/MarkdownExtra.inc.php | 11 + .../php-markdown/Michelf/MarkdownExtra.php | 38 + .../Michelf/MarkdownInterface.inc.php | 9 + .../Michelf/MarkdownInterface.php | 37 + core/lib/michelf/php-markdown/Readme.md | 271 ++ core/lib/michelf/php-markdown/Readme.php | 31 + core/lib/michelf/php-markdown/composer.json | 31 + 10 files changed, 3580 insertions(+) create mode 100644 core/lib/michelf/php-markdown/License.md create mode 100644 core/lib/michelf/php-markdown/Michelf/Markdown.inc.php create mode 100644 core/lib/michelf/php-markdown/Michelf/Markdown.php create mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php create mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownExtra.php create mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php create mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownInterface.php create mode 100644 core/lib/michelf/php-markdown/Readme.md create mode 100644 core/lib/michelf/php-markdown/Readme.php create mode 100644 core/lib/michelf/php-markdown/composer.json diff --git a/core/lib/michelf/php-markdown/License.md b/core/lib/michelf/php-markdown/License.md new file mode 100644 index 000000000..027becbd5 --- /dev/null +++ b/core/lib/michelf/php-markdown/License.md @@ -0,0 +1,36 @@ +PHP Markdown Lib +Copyright (c) 2004-2013 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2006 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php b/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php new file mode 100644 index 000000000..8c281094c --- /dev/null +++ b/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php @@ -0,0 +1,10 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# +namespace Michelf; + + +# +# Markdown Parser Class +# + +class Markdown implements MarkdownInterface { + + ### Version ### + + const MARKDOWNLIB_VERSION = "1.4.0"; + + ### Simple Function Interface ### + + public static function defaultTransform($text) { + # + # Initialize the parser and return the result of its transform method. + # This will work fine for derived classes too. + # + # Take parser class on which this function was called. + $parser_class = \get_called_class(); + + # try to take parser from the static parser list + static $parser_list; + $parser =& $parser_list[$parser_class]; + + # create the parser it not already set + if (!$parser) + $parser = new $parser_class; + + # Transform text using parser. + return $parser->transform($text); + } + + ### Configuration Variables ### + + # Change to ">" for HTML output. + public $empty_element_suffix = " />"; + public $tab_width = 4; + + # Change to `true` to disallow markup or entities. + public $no_markup = false; + public $no_entities = false; + + # Predefined urls and titles for reference links and images. + public $predef_urls = array(); + public $predef_titles = array(); + + + ### Parser Implementation ### + + # Regex to match balanced [brackets]. + # Needed to insert a maximum bracked depth while converting to PHP. + protected $nested_brackets_depth = 6; + protected $nested_brackets_re; + + protected $nested_url_parenthesis_depth = 4; + protected $nested_url_parenthesis_re; + + # Table of hash values for escaped characters: + protected $escape_chars = '\`*_{}[]()>#+-.!'; + protected $escape_chars_re; + + + public function __construct() { + # + # Constructor function. Initialize appropriate member variables. + # + $this->_initDetab(); + $this->prepareItalicsAndBold(); + + $this->nested_brackets_re = + str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). + str_repeat('\])*', $this->nested_brackets_depth); + + $this->nested_url_parenthesis_re = + str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). + str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); + + $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; + + # Sort document, block, and span gamut in ascendent priority order. + asort($this->document_gamut); + asort($this->block_gamut); + asort($this->span_gamut); + } + + + # Internal hashes used during transformation. + protected $urls = array(); + protected $titles = array(); + protected $html_hashes = array(); + + # Status flag to avoid invalid nesting. + protected $in_anchor = false; + + + protected function setup() { + # + # Called before the transformation process starts to setup parser + # states. + # + # Clear global hashes. + $this->urls = $this->predef_urls; + $this->titles = $this->predef_titles; + $this->html_hashes = array(); + + $this->in_anchor = false; + } + + protected function teardown() { + # + # Called after the transformation process to clear any variable + # which may be taking up memory unnecessarly. + # + $this->urls = array(); + $this->titles = array(); + $this->html_hashes = array(); + } + + + public function transform($text) { + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + $this->setup(); + + # Remove UTF-8 BOM and marker character in input, if present. + $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); + + # Standardize line endings: + # DOS to Unix and Mac to Unix + $text = preg_replace('{\r\n?}', "\n", $text); + + # Make sure $text ends with a couple of newlines: + $text .= "\n\n"; + + # Convert all tabs to spaces. + $text = $this->detab($text); + + # Turn block-level HTML blocks into hash entries + $text = $this->hashHTMLBlocks($text); + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ ]*\n+/ . + $text = preg_replace('/^[ ]+$/m', '', $text); + + # Run document gamut methods. + foreach ($this->document_gamut as $method => $priority) { + $text = $this->$method($text); + } + + $this->teardown(); + + return $text . "\n"; + } + + protected $document_gamut = array( + # Strip link definitions, store in hashes. + "stripLinkDefinitions" => 20, + + "runBasicBlockGamut" => 30, + ); + + + protected function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + (?: + <(.+?)> # url = $2 + | + (\S+?) # url = $3 + ) + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $4 + [")] + [ ]* + )? # title is optional + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + protected function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $url = $matches[2] == '' ? $matches[3] : $matches[2]; + $this->urls[$link_id] = $url; + $this->titles[$link_id] =& $matches[4]; + return ''; # String that will replace the block + } + + + protected function hashHTMLBlocks($text) { + if ($this->no_markup) return $text; + + $less_than_tab = $this->tab_width - 1; + + # Hashify HTML blocks: + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded: + # + # * List "a" is made of tags which can be both inline or block-level. + # These will be treated block-level when the start tag is alone on + # its line, otherwise they're not matched here and will be taken as + # inline later. + # * List "b" is made of tags which are always block-level; + # + $block_tags_a_re = 'ins|del'; + $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. + 'script|noscript|form|fieldset|iframe|math|svg|'. + 'article|section|nav|aside|hgroup|header|footer|'. + 'figure'; + + # Regular expression for the content of a block tag. + $nested_tags_level = 4; + $attr = ' + (?> # optional tag attributes + \s # starts with whitespace + (?> + [^>"/]+ # text outside quotes + | + /+(?!>) # slash not followed by ">" + | + "[^"]*" # text inside double quotes (tolerate ">") + | + \'[^\']*\' # text inside single quotes (tolerate ">") + )* + )? + '; + $content = + str_repeat(' + (?> + [^<]+ # content without tag + | + <\2 # nested opening tag + '.$attr.' # attributes + (?> + /> + | + >', $nested_tags_level). # end of opening tag + '.*?'. # last level nested tag content + str_repeat(' + # closing nested tag + ) + | + <(?!/\2\s*> # other tags with a different name + ) + )*', + $nested_tags_level); + $content2 = str_replace('\2', '\3', $content); + + # First, look for nested blocks, e.g.: + #

    + #
    + # tags for inner block must be indented. + #
    + #
    + # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
    ` and stop at the first `
    `. + $text = preg_replace_callback('{(?> + (?> + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + + # Match from `\n` to `\n`, handling nested tags + # in between. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_b_re.')# start tag = $2 + '.$attr.'> # attributes followed by > and \n + '.$content.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special version for tags of group a. + + [ ]{0,'.$less_than_tab.'} + <('.$block_tags_a_re.')# start tag = $3 + '.$attr.'>[ ]*\n # attributes followed by > + '.$content2.' # content, support nesting + # the matching end tag + [ ]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + + | # Special case just for
    . It was easier to make a special + # case than to make the other regex more complicated. + + [ ]{0,'.$less_than_tab.'} + <(hr) # start tag = $2 + '.$attr.' # attributes + /?> # the matching end tag + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # Special case for standalone HTML comments: + + [ ]{0,'.$less_than_tab.'} + (?s: + + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + | # PHP and ASP-style processor instructions ( + ) + [ ]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + + ) + )}Sxmi', + array(&$this, '_hashHTMLBlocks_callback'), + $text); + + return $text; + } + protected function _hashHTMLBlocks_callback($matches) { + $text = $matches[1]; + $key = $this->hashBlock($text); + return "\n\n$key\n\n"; + } + + + protected function hashPart($text, $boundary = 'X') { + # + # Called whenever a tag must be hashed when a function insert an atomic + # element in the text stream. Passing $text to through this function gives + # a unique text-token which will be reverted back when calling unhash. + # + # The $boundary argument specify what character should be used to surround + # the token. By convension, "B" is used for block elements that needs not + # to be wrapped into paragraph tags at the end, ":" is used for elements + # that are word separators and "X" is used in the general case. + # + # Swap back any tag hash found in $text so we do not have to `unhash` + # multiple times at the end. + $text = $this->unhash($text); + + # Then hash the block. + static $i = 0; + $key = "$boundary\x1A" . ++$i . $boundary; + $this->html_hashes[$key] = $text; + return $key; # String that will replace the tag. + } + + + protected function hashBlock($text) { + # + # Shortcut function for hashPart with block-level boundaries. + # + return $this->hashPart($text, 'B'); + } + + + protected $block_gamut = array( + # + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + # + "doHeaders" => 10, + "doHorizontalRules" => 20, + + "doLists" => 40, + "doCodeBlocks" => 50, + "doBlockQuotes" => 60, + ); + + protected function runBlockGamut($text) { + # + # Run block gamut tranformations. + # + # We need to escape raw HTML in Markdown source before doing anything + # else. This need to be done for each block, and not only at the + # begining in the Markdown function since hashed blocks can be part of + # list items and could have been indented. Indented blocks would have + # been seen as a code block in a previous pass of hashHTMLBlocks. + $text = $this->hashHTMLBlocks($text); + + return $this->runBasicBlockGamut($text); + } + + protected function runBasicBlockGamut($text) { + # + # Run block gamut tranformations, without hashing HTML blocks. This is + # useful when HTML blocks are known to be already hashed, like in the first + # whole-document pass. + # + foreach ($this->block_gamut as $method => $priority) { + $text = $this->$method($text); + } + + # Finally form paragraph and restore hashed blocks. + $text = $this->formParagraphs($text); + + return $text; + } + + + protected function doHorizontalRules($text) { + # Do Horizontal Rules: + return preg_replace( + '{ + ^[ ]{0,3} # Leading space + ([-*_]) # $1: First marker + (?> # Repeated marker group + [ ]{0,2} # Zero, one, or two spaces. + \1 # Marker character + ){2,} # Group repeated at least twice + [ ]* # Tailing spaces + $ # End of line. + }mx', + "\n".$this->hashBlock("empty_element_suffix")."\n", + $text); + } + + + protected $span_gamut = array( + # + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + # + # Process character escapes, code spans, and inline HTML + # in one shot. + "parseSpan" => -30, + + # Process anchor and image tags. Images must come first, + # because ![foo][f] looks like an anchor. + "doImages" => 10, + "doAnchors" => 20, + + # Make links out of things like `` + # Must come after doAnchors, because you can use < and > + # delimiters in inline links like [this](). + "doAutoLinks" => 30, + "encodeAmpsAndAngles" => 40, + + "doItalicsAndBold" => 50, + "doHardBreaks" => 60, + ); + + protected function runSpanGamut($text) { + # + # Run span gamut tranformations. + # + foreach ($this->span_gamut as $method => $priority) { + $text = $this->$method($text); + } + + return $text; + } + + + protected function doHardBreaks($text) { + # Do hard breaks: + return preg_replace_callback('/ {2,}\n/', + array(&$this, '_doHardBreaks_callback'), $text); + } + protected function _doHardBreaks_callback($matches) { + return $this->hashPart("empty_element_suffix\n"); + } + + + protected function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ \n]* + (?: + <(.+?)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ \n]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + ) + }xs', + array(&$this, '_doAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link text][1] + # or [link text](/foo) + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ([^\[\]]+) # link text = $2; can\'t contain [ or ] + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + protected function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + protected function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + protected function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ \n]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ \n]* + )? # title is optional + \) + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + protected function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + protected function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + protected function doHeaders($text) { + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + # + $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + protected function _doHeaders_callback_setext($matches) { + # Terrible hack to check we haven't found an empty list item. + if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) + return $matches[0]; + + $level = $matches[2]{0} == '=' ? 1 : 2; + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + protected function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + protected function doLists($text) { + # + # Form HTML ordered (numbered) and unordered (bulleted) lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[\.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $markers_relist = array( + $marker_ul_re => $marker_ol_re, + $marker_ol_re => $marker_ul_re, + ); + + foreach ($markers_relist as $marker_re => $other_marker_re) { + # Re-usable pattern to match any entirel ul or ol list: + $whole_list_re = ' + ( # $1 = whole list + ( # $2 + ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces + ('.$marker_re.') # $4 = first list item marker + [ ]+ + ) + (?s:.+?) + ( # $5 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ ]* + '.$marker_re.'[ ]+ + ) + | + (?= # Lookahead for another kind of list + \n + \3 # Must have the same indentation + '.$other_marker_re.'[ ]+ + ) + ) + ) + '; // mx + + # We use a different prefix before nested lists than top-level lists. + # See extended comment in _ProcessListItems(). + + if ($this->list_level) { + $text = preg_replace_callback('{ + ^ + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + else { + $text = preg_replace_callback('{ + (?:(?<=\n)\n|\A\n?) # Must eat the newline + '.$whole_list_re.' + }mx', + array(&$this, '_doLists_callback'), $text); + } + } + + return $text; + } + protected function _doLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $marker_ul_re = '[*+-]'; + $marker_ol_re = '\d+[\.]'; + $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; + + $list = $matches[1]; + $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol"; + + $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); + + $list .= "\n"; + $result = $this->processListItems($list, $marker_any_re); + + $result = $this->hashBlock("<$list_type>\n" . $result . ""); + return "\n". $result ."\n\n"; + } + + protected $list_level = 0; + + protected function processListItems($list_str, $marker_any_re) { + # + # Process the contents of a single ordered or unordered list, splitting it + # into individual list items. + # + # The $this->list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + + $this->list_level++; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + $list_str = preg_replace_callback('{ + (\n)? # leading line = $1 + (^[ ]*) # leading whitespace = $2 + ('.$marker_any_re.' # list marker and space = $3 + (?:[ ]+|(?=\n)) # space only required if item is not empty + ) + ((?s:.*?)) # list item text = $4 + (?:(\n+(?=\n))|\n) # tailing blank line = $5 + (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) + }xm', + array(&$this, '_processListItems_callback'), $list_str); + + $this->list_level--; + return $list_str; + } + protected function _processListItems_callback($matches) { + $item = $matches[4]; + $leading_line =& $matches[1]; + $leading_space =& $matches[2]; + $marker_space = $matches[3]; + $tailing_blank_line =& $matches[5]; + + if ($leading_line || $tailing_blank_line || + preg_match('/\n{2,}/', $item)) + { + # Replace marker with the appropriate whitespace indentation + $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; + $item = $this->runBlockGamut($this->outdent($item)."\n"); + } + else { + # Recursion for sub-lists: + $item = $this->doLists($this->outdent($item)); + $item = preg_replace('/\n+$/', '', $item); + $item = $this->runSpanGamut($item); + } + + return "
  • " . $item . "
  • \n"; + } + + + protected function doCodeBlocks($text) { + # + # Process Markdown `
    ` blocks.
    +	#
    +		$text = preg_replace_callback('{
    +				(?:\n\n|\A\n?)
    +				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    +				  (?>
    +					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    +					.*\n+
    +				  )+
    +				)
    +				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    +			}xm',
    +			array(&$this, '_doCodeBlocks_callback'), $text);
    +
    +		return $text;
    +	}
    +	protected function _doCodeBlocks_callback($matches) {
    +		$codeblock = $matches[1];
    +
    +		$codeblock = $this->outdent($codeblock);
    +		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    +
    +		# trim leading newlines and trailing newlines
    +		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    +
    +		$codeblock = "
    $codeblock\n
    "; + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + + + protected function makeCodeSpan($code) { + # + # Create a code span markup for $code. Called from handleSpanToken. + # + $code = htmlspecialchars(trim($code), ENT_NOQUOTES); + return $this->hashPart("$code"); + } + + + protected $em_relist = array( + '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { + foreach ($this->strong_relist as $strong => $strong_re) { + # Construct list of allowed token expressions. + $token_relist = array(); + if (isset($this->em_strong_relist["$em$strong"])) { + $token_relist[] = $this->em_strong_relist["$em$strong"]; + } + $token_relist[] = $em_re; + $token_relist[] = $strong_re; + + # Construct master expression from list. + $token_re = '{('. implode('|', $token_relist) .')}'; + $this->em_strong_prepared_relist["$em$strong"] = $token_re; + } + } + } + + protected function doItalicsAndBold($text) { + $token_stack = array(''); + $text_stack = array(''); + $em = ''; + $strong = ''; + $tree_char_em = false; + + while (1) { + # + # Get prepared regular expression for seraching emphasis tokens + # in current context. + # + $token_re = $this->em_strong_prepared_relist["$em$strong"]; + + # + # Each loop iteration search for the next emphasis token. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + $text_stack[0] .= $parts[0]; + $token =& $parts[1]; + $text =& $parts[2]; + + if (empty($token)) { + # Reached end of text span: empty stack without emitting. + # any more emphasis. + while ($token_stack[0]) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + break; + } + + $token_len = strlen($token); + if ($tree_char_em) { + # Reached closing marker while inside a three-char emphasis. + if ($token_len == 3) { + # Three-char closing marker, close em and strong. + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + $strong = ''; + } else { + # Other closing marker: close one em or strong and + # change current token state to match the other + $token_stack[0] = str_repeat($token{0}, 3-$token_len); + $tag = $token_len == 2 ? "strong" : "em"; + $span = $text_stack[0]; + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] = $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + $tree_char_em = false; + } else if ($token_len == 3) { + if ($em) { + # Reached closing marker for both em and strong. + # Closing strong marker: + for ($i = 0; $i < 2; ++$i) { + $shifted_token = array_shift($token_stack); + $tag = strlen($shifted_token) == 2 ? "strong" : "em"; + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "<$tag>$span"; + $text_stack[0] .= $this->hashPart($span); + $$tag = ''; # $$tag stands for $em or $strong + } + } else { + # Reached opening three-char emphasis marker. Push on token + # stack; will be handled by the special condition above. + $em = $token{0}; + $strong = "$em$em"; + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $tree_char_em = true; + } + } else if ($token_len == 2) { + if ($strong) { + # Unwind any dangling emphasis marker: + if (strlen($token_stack[0]) == 1) { + $text_stack[1] .= array_shift($token_stack); + $text_stack[0] .= array_shift($text_stack); + } + # Closing strong marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $strong = ''; + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $strong = $token; + } + } else { + # Here $token_len == 1 + if ($em) { + if (strlen($token_stack[0]) == 1) { + # Closing emphasis marker: + array_shift($token_stack); + $span = array_shift($text_stack); + $span = $this->runSpanGamut($span); + $span = "$span"; + $text_stack[0] .= $this->hashPart($span); + $em = ''; + } else { + $text_stack[0] .= $token; + } + } else { + array_unshift($token_stack, $token); + array_unshift($text_stack, ''); + $em = $token; + } + } + } + return $text_stack[0]; + } + + + protected function doBlockQuotes($text) { + $text = preg_replace_callback('/ + ( # Wrap whole match in $1 + (?> + ^[ ]*>[ ]? # ">" at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + /xm', + array(&$this, '_doBlockQuotes_callback'), $text); + + return $text; + } + protected function _doBlockQuotes_callback($matches) { + $bq = $matches[1]; + # trim one level of quoting - trim whitespace-only lines + $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); + $bq = $this->runBlockGamut($bq); # recurse + + $bq = preg_replace('/^/m', " ", $bq); + # These leading spaces cause problem with
     content, 
    +		# so we need to fix that:
    +		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', + array(&$this, '_doBlockQuotes_callback2'), $bq); + + return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; + } + protected function _doBlockQuotes_callback2($matches) { + $pre = $matches[1]; + $pre = preg_replace('/^ /m', '', $pre); + return $pre; + } + + + protected function formParagraphs($text) { + # + # Params: + # $text - string to process with html

    tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { + # Is a paragraph. + $value = $this->runSpanGamut($value); + $value = preg_replace('/^([ ]*)/', "

    ", $value); + $value .= "

    "; + $grafs[$key] = $this->unhash($value); + } + else { + # Is a block. + # Modify elements of @grafs in-place... + $graf = $value; + $block = $this->html_hashes[$graf]; + $graf = $block; +// if (preg_match('{ +// \A +// ( # $1 =
    tag +//
    ]* +// \b +// markdown\s*=\s* ([\'"]) # $2 = attr quote char +// 1 +// \2 +// [^>]* +// > +// ) +// ( # $3 = contents +// .* +// ) +// (
    ) # $4 = closing tag +// \z +// }xs', $block, $matches)) +// { +// list(, $div_open, , $div_content, $div_close) = $matches; +// +// # We can't call Markdown(), because that resets the hash; +// # that initialization code should be pulled into its own sub, though. +// $div_content = $this->hashHTMLBlocks($div_content); +// +// # Run document gamut methods on the content. +// foreach ($this->document_gamut as $method => $priority) { +// $div_content = $this->$method($div_content); +// } +// +// $div_open = preg_replace( +// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); +// +// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; +// } + $grafs[$key] = $graf; + } + } + + return implode("\n\n", $grafs); + } + + + protected function encodeAttribute($text) { + # + # Encode text for a double-quoted HTML attribute. This function + # is *not* suitable for attributes enclosed in single quotes. + # + $text = $this->encodeAmpsAndAngles($text); + $text = str_replace('"', '"', $text); + return $text; + } + + + protected function encodeAmpsAndAngles($text) { + # + # Smart processing for ampersands and angle brackets that need to + # be encoded. Valid character entities are left alone unless the + # no-entities mode is set. + # + if ($this->no_entities) { + $text = str_replace('&', '&', $text); + } else { + # Ampersand-encoding based entirely on Nat Irons's Amputator + # MT plugin: + $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', + '&', $text); + } + # Encode remaining <'s + $text = str_replace('<', '<', $text); + + return $text; + } + + + protected function doAutoLinks($text) { + $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', + array(&$this, '_doAutoLinks_url_callback'), $text); + + # Email addresses: + $text = preg_replace_callback('{ + < + (?:mailto:)? + ( + (?: + [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+ + | + ".*?" + ) + \@ + (?: + [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ + | + \[[\d.a-fA-F:]+\] # IPv4 & IPv6 + ) + ) + > + }xi', + array(&$this, '_doAutoLinks_email_callback'), $text); + $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text); + + return $text; + } + protected function _doAutoLinks_tel_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $tel = $this->encodeAttribute($matches[2]); + $link = "$tel"; + return $this->hashPart($link); + } + protected function _doAutoLinks_url_callback($matches) { + $url = $this->encodeAttribute($matches[1]); + $link = "$url"; + return $this->hashPart($link); + } + protected function _doAutoLinks_email_callback($matches) { + $address = $matches[1]; + $link = $this->encodeEmailAddress($address); + return $this->hashPart($link); + } + + + protected function encodeEmailAddress($addr) { + # + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + #

    foo@exampl + # e.com

    + # + # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. + # With some optimizations by Milian Wolff. + # + $addr = "mailto:" . $addr; + $chars = preg_split('/(? $char) { + $ord = ord($char); + # Ignore non-ascii chars. + if ($ord < 128) { + $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. + # roughly 10% raw, 45% hex, 45% dec + # '@' *must* be encoded. I insist. + if ($r > 90 && $char != '@') /* do nothing */; + else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; + else $chars[$key] = '&#'.$ord.';'; + } + } + + $addr = implode('', $chars); + $text = implode('', array_slice($chars, 7)); # text without `mailto:` + $addr = "$text"; + + return $addr; + } + + + protected function parseSpan($str) { + # + # Take the string $str and parse it into tokens, hashing embeded HTML, + # escaped characters and handling code spans. + # + $output = ''; + + $span_re = '{ + ( + \\\\'.$this->escape_chars_re.' + | + (?no_markup ? '' : ' + | + # comment + | + <\?.*?\?> | <%.*?%> # processing instruction + | + <[!$]?[-a-zA-Z0-9:_]+ # regular tags + (?> + \s + (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* + )? + > + | + <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag + | + # closing tag + ').' + ) + }xs'; + + while (1) { + # + # Each loop iteration seach for either the next tag, the next + # openning code span marker, or the next escaped character. + # Each token is then passed to handleSpanToken. + # + $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); + + # Create token from text preceding tag. + if ($parts[0] != "") { + $output .= $parts[0]; + } + + # Check if we reach the end. + if (isset($parts[1])) { + $output .= $this->handleSpanToken($parts[1], $parts[2]); + $str = $parts[2]; + } + else { + break; + } + } + + return $output; + } + + + protected function handleSpanToken($token, &$str) { + # + # Handle $token provided by parseSpan by determining its nature and + # returning the corresponding value that should replace it. + # + switch ($token{0}) { + case "\\": + return $this->hashPart("&#". ord($token{1}). ";"); + case "`": + # Search for end marker in remaining text. + if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', + $str, $matches)) + { + $str = $matches[2]; + $codespan = $this->makeCodeSpan($matches[1]); + return $this->hashPart($codespan); + } + return $token; // return as text since no ending marker found. + default: + return $this->hashPart($token); + } + } + + + protected function outdent($text) { + # + # Remove one level of line-leading tabs or spaces + # + return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); + } + + + # String length function for detab. `_initDetab` will create a function to + # hanlde UTF-8 if the default function does not exist. + protected $utf8_strlen = 'mb_strlen'; + + protected function detab($text) { + # + # Replace tabs with the appropriate amount of space. + # + # For each line we separate the line in blocks delemited by + # tab characters. Then we reconstruct every line by adding the + # appropriate number of space between each blocks. + + $text = preg_replace_callback('/^.*\t.*$/m', + array(&$this, '_detab_callback'), $text); + + return $text; + } + protected function _detab_callback($matches) { + $line = $matches[0]; + $strlen = $this->utf8_strlen; # strlen function for UTF-8. + + # Split in blocks. + $blocks = explode("\t", $line); + # Add each blocks to the line. + $line = $blocks[0]; + unset($blocks[0]); # Do not add first block twice. + foreach ($blocks as $block) { + # Calculate amount of space, insert spaces, insert block. + $amount = $this->tab_width - + $strlen($line, 'UTF-8') % $this->tab_width; + $line .= str_repeat(" ", $amount) . $block; + } + return $line; + } + protected function _initDetab() { + # + # Check for the availability of the function in the `utf8_strlen` property + # (initially `mb_strlen`). If the function is not available, create a + # function that will loosely count the number of UTF-8 characters with a + # regular expression. + # + if (function_exists($this->utf8_strlen)) return; + $this->utf8_strlen = create_function('$text', 'return preg_match_all( + "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", + $text, $m);'); + } + + + protected function unhash($text) { + # + # Swap back in all the tags hashed by _HashHTMLBlocks. + # + return preg_replace_callback('/(.)\x1A[0-9]+\1/', + array(&$this, '_unhash_callback'), $text); + } + protected function _unhash_callback($matches) { + return $this->html_hashes[$matches[0]]; + } + +} + + +# +# Temporary Markdown Extra Parser Implementation Class +# +# NOTE: DON'T USE THIS CLASS +# Currently the implementation of of Extra resides here in this temporary class. +# This makes it easier to propagate the changes between the three different +# packaging styles of PHP Markdown. When this issue is resolved, this +# MarkdownExtra_TmpImpl class here will disappear and \Michelf\MarkdownExtra +# will contain the code. So please use \Michelf\MarkdownExtra and ignore this +# one. +# + +abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { + + ### Configuration Variables ### + + # Prefix for footnote ids. + public $fn_id_prefix = ""; + + # Optional title attribute for footnote links and backlinks. + public $fn_link_title = ""; + public $fn_backlink_title = ""; + + # Optional class attribute for footnote links and backlinks. + public $fn_link_class = "footnote-ref"; + public $fn_backlink_class = "footnote-backref"; + + # Class name for table cell alignment (%% replaced left/center/right) + # For instance: 'go-%%' becomes 'go-left' or 'go-right' or 'go-center' + # If empty, the align attribute is used instead of a class name. + public $table_align_class_tmpl = ''; + + # Optional class prefix for fenced code block. + public $code_class_prefix = ""; + # Class attribute for code blocks goes on the `code` tag; + # setting this to true will put attributes on the `pre` tag instead. + public $code_attr_on_pre = false; + + # Predefined abbreviations. + public $predef_abbr = array(); + + + ### Parser Implementation ### + + public function __construct() { + # + # Constructor function. Initialize the parser object. + # + # Add extra escapable characters before parent constructor + # initialize the table. + $this->escape_chars .= ':|'; + + # Insert extra document, block, and span transformations. + # Parent constructor will do the sorting. + $this->document_gamut += array( + "doFencedCodeBlocks" => 5, + "stripFootnotes" => 15, + "stripAbbreviations" => 25, + "appendFootnotes" => 50, + ); + $this->block_gamut += array( + "doFencedCodeBlocks" => 5, + "doTables" => 15, + "doDefLists" => 45, + ); + $this->span_gamut += array( + "doFootnotes" => 5, + "doAbbreviations" => 70, + ); + + parent::__construct(); + } + + + # Extra variables used during extra transformations. + protected $footnotes = array(); + protected $footnotes_ordered = array(); + protected $footnotes_ref_count = array(); + protected $footnotes_numbers = array(); + protected $abbr_desciptions = array(); + protected $abbr_word_re = ''; + + # Give the current footnote number. + protected $footnote_counter = 1; + + + protected function setup() { + # + # Setting up Extra-specific variables. + # + parent::setup(); + + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->footnotes_ref_count = array(); + $this->footnotes_numbers = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + $this->footnote_counter = 1; + + foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + } + } + + protected function teardown() { + # + # Clearing Extra-specific variables. + # + $this->footnotes = array(); + $this->footnotes_ordered = array(); + $this->footnotes_ref_count = array(); + $this->footnotes_numbers = array(); + $this->abbr_desciptions = array(); + $this->abbr_word_re = ''; + + parent::teardown(); + } + + + ### Extra Attribute Parser ### + + # Expression to use to catch attributes (includes the braces) + protected $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}'; + # Expression to use when parsing in a context when no capture is desired + protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}'; + + protected function doExtraAttributes($tag_name, $attr) { + # + # Parse attributes caught by the $this->id_class_attr_catch_re expression + # and return the HTML-formatted list of attributes. + # + # Currently supported attributes are .class and #id. + # + if (empty($attr)) return ""; + + # Split on components + preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches); + $elements = $matches[0]; + + # handle classes and ids (only first id taken into account) + $classes = array(); + $id = false; + foreach ($elements as $element) { + if ($element{0} == '.') { + $classes[] = substr($element, 1); + } else if ($element{0} == '#') { + if ($id === false) $id = substr($element, 1); + } + } + + # compose attributes as string + $attr_str = ""; + if (!empty($id)) { + $attr_str .= ' id="'.$id.'"'; + } + if (!empty($classes)) { + $attr_str .= ' class="'.implode(" ", $classes).'"'; + } + return $attr_str; + } + + + protected function stripLinkDefinitions($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: ^[id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 + [ ]* + \n? # maybe *one* newline + [ ]* + (?: + <(.+?)> # url = $2 + | + (\S+?) # url = $3 + ) + [ ]* + \n? # maybe one newline + [ ]* + (?: + (?<=\s) # lookbehind for whitespace + ["(] + (.*?) # title = $4 + [")] + [ ]* + )? # title is optional + (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr + (?:\n+|\Z) + }xm', + array(&$this, '_stripLinkDefinitions_callback'), + $text); + return $text; + } + protected function _stripLinkDefinitions_callback($matches) { + $link_id = strtolower($matches[1]); + $url = $matches[2] == '' ? $matches[3] : $matches[2]; + $this->urls[$link_id] = $url; + $this->titles[$link_id] =& $matches[4]; + $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]); + return ''; # String that will replace the block + } + + + ### HTML Block Parser ### + + # Tags that are always treated as block tags: + protected $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption'; + + # Tags treated as block tags only if the opening tag is alone on its line: + protected $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video'; + + # Tags where markdown="1" default to span mode: + protected $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; + + # Tags which must not have their contents modified, no matter where + # they appear: + protected $clean_tags_re = 'script|math|svg'; + + # Tags that do not need to be closed. + protected $auto_close_tags_re = 'hr|img|param|source|track'; + + + protected function hashHTMLBlocks($text) { + # + # Hashify HTML Blocks and "clean tags". + # + # We only want to do this for block-level HTML tags, such as headers, + # lists, and tables. That's because we still want to wrap

    s around + # "paragraphs" that are wrapped in non-block-level tags, such as anchors, + # phrase emphasis, and spans. The list of tags we're looking for is + # hard-coded. + # + # This works by calling _HashHTMLBlocks_InMarkdown, which then calls + # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" + # attribute is found within a tag, _HashHTMLBlocks_InHTML calls back + # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. + # These two functions are calling each other. It's recursive! + # + if ($this->no_markup) return $text; + + # + # Call the HTML-in-Markdown hasher. + # + list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); + + return $text; + } + protected function _hashHTMLBlocks_inMarkdown($text, $indent = 0, + $enclosing_tag_re = '', $span = false) + { + # + # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. + # + # * $indent is the number of space to be ignored when checking for code + # blocks. This is important because if we don't take the indent into + # account, something like this (which looks right) won't work as expected: + # + #

    + #
    + # Hello World. <-- Is this a Markdown code block or text? + #
    <-- Is this a Markdown code block or a real tag? + #
    + # + # If you don't like this, just don't indent the tag on which + # you apply the markdown="1" attribute. + # + # * If $enclosing_tag_re is not empty, stops at the first unmatched closing + # tag with that name. Nested tags supported. + # + # * If $span is true, text inside must treated as span. So any double + # newline will be replaced by a single newline so that it does not create + # paragraphs. + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to check for the presense of newlines around a block tag. + $newline_before_re = '/(?:^\n?|\n\n)*$/'; + $newline_after_re = + '{ + ^ # Start of text following the tag. + (?>[ ]*)? # Optional comment. + [ ]*\n # Must be followed by newline. + }xs'; + + # Regex to match any tag. + $block_tag_re = + '{ + ( # $2: Capture whole tag. + # Tag name. + '.$this->block_tags_re.' | + '.$this->context_block_tags_re.' | + '.$this->clean_tags_re.' | + (?!\s)'.$enclosing_tag_re.' + ) + (?: + (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name. + (?> + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + '. ( !$span ? ' # If not in span. + | + # Indented code block + (?: ^[ ]*\n | ^ | \n[ ]*\n ) + [ ]{'.($indent+4).'}[^\n]* \n + (?> + (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n + )* + | + # Fenced code block marker + (?<= ^ | \n ) + [ ]{0,'.($indent+3).'}(?:~{3,}|`{3,}) + [ ]* + (?: + \.?[-_:a-zA-Z0-9]+ # standalone class name + | + '.$this->id_class_attr_nocatch_re.' # extra attributes + )? + [ ]* + (?= \n ) + ' : '' ). ' # End (if not is span). + | + # Code span marker + # Note, this regex needs to go after backtick fenced + # code blocks but it should also be kept outside of the + # "if not in span" condition adding backticks to the parser + `+ + ) + }xs'; + + + $depth = 0; # Current depth inside the tag tree. + $parsed = ""; # Parsed text that will be returned. + + # + # Loop through every tag until we find the closing tag of the parent + # or loop until reaching the end of text if no parent tag specified. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($block_tag_re, $text, 2, + PREG_SPLIT_DELIM_CAPTURE); + + # If in Markdown span mode, add a empty-string span-level hash + # after each newline to prevent triggering any block element. + if ($span) { + $void = $this->hashPart("", ':'); + $newline = "$void\n"; + $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; + } + + $parsed .= $parts[0]; # Text before current tag. + + # If end of $text has been reached. Stop loop. + if (count($parts) < 3) { + $text = ""; + break; + } + + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + $tag_re = preg_quote($tag); # For use in a regular expression. + + # + # Check for: Fenced code block marker. + # Note: need to recheck the whole tag to disambiguate backtick + # fences from code spans + # + if (preg_match('{^\n?([ ]{0,'.($indent+3).'})(~{3,}|`{3,})[ ]*(?:\.?[-_:a-zA-Z0-9]+|'.$this->id_class_attr_nocatch_re.')?[ ]*\n?$}', $tag, $capture)) { + # Fenced code block marker: find matching end marker. + $fence_indent = strlen($capture[1]); # use captured indent in re + $fence_re = $capture[2]; # use captured fence in re + if (preg_match('{^(?>.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text, + $matches)) + { + # End marker found: pass text unchanged until marker. + $parsed .= $tag . $matches[0]; + $text = substr($text, strlen($matches[0])); + } + else { + # No end marker: just skip it. + $parsed .= $tag; + } + } + # + # Check for: Indented code block. + # + else if ($tag{0} == "\n" || $tag{0} == " ") { + # Indented code block: pass it unchanged, will be handled + # later. + $parsed .= $tag; + } + # + # Check for: Code span marker + # Note: need to check this after backtick fenced code blocks + # + else if ($tag{0} == "`") { + # Find corresponding end marker. + $tag_re = preg_quote($tag); + if (preg_match('{^(?>.+?|\n(?!\n))*?(?block_tags_re.')\b}', $tag) || + ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) && + preg_match($newline_before_re, $parsed) && + preg_match($newline_after_re, $text) ) + ) + { + # Need to parse tag and following text using the HTML parser. + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); + + # Make sure it stays outside of any paragraph by adding newlines. + $parsed .= "\n\n$block_text\n\n"; + } + # + # Check for: Clean tag (like script, math) + # HTML Comments, processing instructions. + # + else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Need to parse tag and following text using the HTML parser. + # (don't check for markdown attribute) + list($block_text, $text) = + $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); + + $parsed .= $block_text; + } + # + # Check for: Tag with same name as enclosing tag. + # + else if ($enclosing_tag_re !== '' && + # Same name as enclosing tag. + preg_match('{^= 0); + + return array($parsed, $text); + } + protected function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { + # + # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. + # + # * Calls $hash_method to convert any blocks. + # * Stops when the first opening tag closes. + # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. + # (it is not inside clean tags) + # + # Returns an array of that form: ( processed text , remaining text ) + # + if ($text === '') return array('', ''); + + # Regex to match `markdown` attribute inside of a tag. + $markdown_attr_re = ' + { + \s* # Eat whitespace before the `markdown` attribute + markdown + \s*=\s* + (?> + (["\']) # $1: quote delimiter + (.*?) # $2: attribute value + \1 # matching delimiter + | + ([^\s>]*) # $3: unquoted attribute value + ) + () # $4: make $3 always defined (avoid warnings) + }xs'; + + # Regex to match any tag. + $tag_re = '{ + ( # $2: Capture whole tag. + + ".*?" | # Double quotes (can contain `>`) + \'.*?\' | # Single quotes (can contain `>`) + .+? # Anything but quotes and `>`. + )*? + )? + > # End of tag. + | + # HTML Comment + | + <\?.*?\?> | <%.*?%> # Processing instruction + | + # CData Block + ) + }xs'; + + $original_text = $text; # Save original text in case of faliure. + + $depth = 0; # Current depth inside the tag tree. + $block_text = ""; # Temporary text holder for current text. + $parsed = ""; # Parsed text that will be returned. + + # + # Get the name of the starting tag. + # (This pattern makes $base_tag_name_re safe without quoting.) + # + if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) + $base_tag_name_re = $matches[1]; + + # + # Loop through every tag until we find the corresponding closing tag. + # + do { + # + # Split the text using the first $tag_match pattern found. + # Text before pattern will be first in the array, text after + # pattern will be at the end, and between will be any catches made + # by the pattern. + # + $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); + + if (count($parts) < 3) { + # + # End of $text reached with unbalenced tag(s). + # In that case, we return original text unchanged and pass the + # first character as filtered to prevent an infinite loop in the + # parent function. + # + return array($original_text{0}, substr($original_text, 1)); + } + + $block_text .= $parts[0]; # Text before current tag. + $tag = $parts[1]; # Tag to handle. + $text = $parts[2]; # Remaining text after current tag. + + # + # Check for: Auto-close tag (like
    ) + # Comments and Processing Instructions. + # + if (preg_match('{^auto_close_tags_re.')\b}', $tag) || + $tag{1} == '!' || $tag{1} == '?') + { + # Just add the tag to the block as if it was text. + $block_text .= $tag; + } + else { + # + # Increase/decrease nested tag count. Only do so if + # the tag's name match base tag's. + # + if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; + $span_mode = $this->mode == 'span' || $this->mode != 'block' && + preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); + + # Calculate indent before tag. + if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { + $strlen = $this->utf8_strlen; + $indent = $strlen($matches[1], 'UTF-8'); + } else { + $indent = 0; + } + + # End preceding block with this tag. + $block_text .= $tag; + $parsed .= $this->$hash_method($block_text); + + # Get enclosing tag name for the ParseMarkdown function. + # (This pattern makes $tag_name_re safe without quoting.) + preg_match('/^<([\w:$]*)\b/', $tag, $matches); + $tag_name_re = $matches[1]; + + # Parse the content using the HTML-in-Markdown parser. + list ($block_text, $text) + = $this->_hashHTMLBlocks_inMarkdown($text, $indent, + $tag_name_re, $span_mode); + + # Outdent markdown text. + if ($indent > 0) { + $block_text = preg_replace("/^[ ]{1,$indent}/m", "", + $block_text); + } + + # Append tag content to parsed text. + if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; + else $parsed .= "$block_text"; + + # Start over with a new block. + $block_text = ""; + } + else $block_text .= $tag; + } + + } while ($depth > 0); + + # + # Hash last block text that wasn't processed inside the loop. + # + $parsed .= $this->$hash_method($block_text); + + return array($parsed, $text); + } + + + protected function hashClean($text) { + # + # Called whenever a tag must be hashed when a function inserts a "clean" tag + # in $text, it passes through this function and is automaticaly escaped, + # blocking invalid nested overlap. + # + return $this->hashPart($text, 'C'); + } + + + protected function doAnchors($text) { + # + # Turn Markdown link shortcuts into XHTML tags. + # + if ($this->in_anchor) return $text; + $this->in_anchor = true; + + # + # First, handle reference-style links: [link text] [id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + # + # Next, inline-style links: [link text](url "optional title") + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ('.$this->nested_brackets_re.') # link text = $2 + \] + \( # literal paren + [ \n]* + (?: + <(.+?)> # href = $3 + | + ('.$this->nested_url_parenthesis_re.') # href = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # Title = $7 + \6 # matching quote + [ \n]* # ignore any spaces/tabs between closing quote and ) + )? # title is optional + \) + (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes + ) + }xs', + array(&$this, '_doAnchors_inline_callback'), $text); + + # + # Last, handle reference-style shortcuts: [link text] + # These must come last in case you've also got [link text][1] + # or [link text](/foo) + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + \[ + ([^\[\]]+) # link text = $2; can\'t contain [ or ] + \] + ) + }xs', + array(&$this, '_doAnchors_reference_callback'), $text); + + $this->in_anchor = false; + return $text; + } + protected function _doAnchors_reference_callback($matches) { + $whole_match = $matches[1]; + $link_text = $matches[2]; + $link_id =& $matches[3]; + + if ($link_id == "") { + # for shortcut links like [this][] or [this]. + $link_id = $link_text; + } + + # lower-case and turn embedded newlines into spaces + $link_id = strtolower($link_id); + $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); + + if (isset($this->urls[$link_id])) { + $url = $this->urls[$link_id]; + $url = $this->encodeAttribute($url); + + $result = "titles[$link_id] ) ) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + if (isset($this->ref_attr[$link_id])) + $result .= $this->ref_attr[$link_id]; + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + $result = $this->hashPart($result); + } + else { + $result = $whole_match; + } + return $result; + } + protected function _doAnchors_inline_callback($matches) { + $whole_match = $matches[1]; + $link_text = $this->runSpanGamut($matches[2]); + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); + + + $url = $this->encodeAttribute($url); + + $result = "encodeAttribute($title); + $result .= " title=\"$title\""; + } + $result .= $attr; + + $link_text = $this->runSpanGamut($link_text); + $result .= ">$link_text"; + + return $this->hashPart($result); + } + + + protected function doImages($text) { + # + # Turn Markdown image shortcuts into tags. + # + # + # First, handle reference-style labeled images: ![alt text][id] + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + + \[ + (.*?) # id = $3 + \] + + ) + }xs', + array(&$this, '_doImages_reference_callback'), $text); + + # + # Next, handle inline images: ![alt text](url "optional title") + # Don't forget: encode * and _ + # + $text = preg_replace_callback('{ + ( # wrap whole match in $1 + !\[ + ('.$this->nested_brackets_re.') # alt text = $2 + \] + \s? # One optional whitespace character + \( # literal paren + [ \n]* + (?: + <(\S*)> # src url = $3 + | + ('.$this->nested_url_parenthesis_re.') # src url = $4 + ) + [ \n]* + ( # $5 + ([\'"]) # quote char = $6 + (.*?) # title = $7 + \6 # matching quote + [ \n]* + )? # title is optional + \) + (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes + ) + }xs', + array(&$this, '_doImages_inline_callback'), $text); + + return $text; + } + protected function _doImages_reference_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $link_id = strtolower($matches[3]); + + if ($link_id == "") { + $link_id = strtolower($alt_text); # for shortcut links like ![this][]. + } + + $alt_text = $this->encodeAttribute($alt_text); + if (isset($this->urls[$link_id])) { + $url = $this->encodeAttribute($this->urls[$link_id]); + $result = "\"$alt_text\"";titles[$link_id])) { + $title = $this->titles[$link_id]; + $title = $this->encodeAttribute($title); + $result .= " title=\"$title\""; + } + if (isset($this->ref_attr[$link_id])) + $result .= $this->ref_attr[$link_id]; + $result .= $this->empty_element_suffix; + $result = $this->hashPart($result); + } + else { + # If there's no such link ID, leave intact: + $result = $whole_match; + } + + return $result; + } + protected function _doImages_inline_callback($matches) { + $whole_match = $matches[1]; + $alt_text = $matches[2]; + $url = $matches[3] == '' ? $matches[4] : $matches[3]; + $title =& $matches[7]; + $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); + + $alt_text = $this->encodeAttribute($alt_text); + $url = $this->encodeAttribute($url); + $result = "\"$alt_text\"";encodeAttribute($title); + $result .= " title=\"$title\""; # $title already quoted + } + $result .= $attr; + $result .= $this->empty_element_suffix; + + return $this->hashPart($result); + } + + + protected function doHeaders($text) { + # + # Redefined to add id and class attribute support. + # + # Setext-style headers: + # Header 1 {#header1} + # ======== + # + # Header 2 {#header2 .class1 .class2} + # -------- + # + $text = preg_replace_callback( + '{ + (^.+?) # $1: Header text + (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes + [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer + }mx', + array(&$this, '_doHeaders_callback_setext'), $text); + + # atx-style headers: + # # Header 1 {#header1} + # ## Header 2 {#header2} + # ## Header 2 with closing hashes ## {#header3.class1.class2} + # ... + # ###### Header 6 {.class2} + # + $text = preg_replace_callback('{ + ^(\#{1,6}) # $1 = string of #\'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #\'s (not counted) + (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes + [ ]* + \n+ + }xm', + array(&$this, '_doHeaders_callback_atx'), $text); + + return $text; + } + protected function _doHeaders_callback_setext($matches) { + if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) + return $matches[0]; + $level = $matches[3]{0} == '=' ? 1 : 2; + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]); + $block = "".$this->runSpanGamut($matches[1]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + protected function _doHeaders_callback_atx($matches) { + $level = strlen($matches[1]); + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]); + $block = "".$this->runSpanGamut($matches[2]).""; + return "\n" . $this->hashBlock($block) . "\n\n"; + } + + + protected function doTables($text) { + # + # Form HTML tables. + # + $less_than_tab = $this->tab_width - 1; + # + # Find tables with leading pipe. + # + # | Header 1 | Header 2 + # | -------- | -------- + # | Cell 1 | Cell 2 + # | Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] # Optional leading pipe (present) + (.+) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + [ ]* # Allowed whitespace. + [|] .* \n # Row content. + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_doTable_leadingPipe_callback'), $text); + + # + # Find tables without leading pipe. + # + # Header 1 | Header 2 + # -------- | -------- + # Cell 1 | Cell 2 + # Cell 3 | Cell 4 + # + $text = preg_replace_callback(' + { + ^ # Start of a line + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + (\S.*[|].*) \n # $1: Header row (at least one pipe) + + [ ]{0,'.$less_than_tab.'} # Allowed whitespace. + ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline + + ( # $3: Cells + (?> + .* [|] .* \n # Row content + )* + ) + (?=\n|\Z) # Stop at final double newline. + }xm', + array(&$this, '_DoTable_callback'), $text); + + return $text; + } + protected function _doTable_leadingPipe_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove leading pipe for each row. + $content = preg_replace('/^ *[|]/m', '', $content); + + return $this->_doTable_callback(array($matches[0], $head, $underline, $content)); + } + protected function _doTable_makeAlignAttr($alignname) + { + if (empty($this->table_align_class_tmpl)) + return " align=\"$alignname\""; + + $classname = str_replace('%%', $alignname, $this->table_align_class_tmpl); + return " class=\"$classname\""; + } + protected function _doTable_callback($matches) { + $head = $matches[1]; + $underline = $matches[2]; + $content = $matches[3]; + + # Remove any tailing pipes for each line. + $head = preg_replace('/[|] *$/m', '', $head); + $underline = preg_replace('/[|] *$/m', '', $underline); + $content = preg_replace('/[|] *$/m', '', $content); + + # Reading alignement from header underline. + $separators = preg_split('/ *[|] */', $underline); + foreach ($separators as $n => $s) { + if (preg_match('/^ *-+: *$/', $s)) + $attr[$n] = $this->_doTable_makeAlignAttr('right'); + else if (preg_match('/^ *:-+: *$/', $s)) + $attr[$n] = $this->_doTable_makeAlignAttr('center'); + else if (preg_match('/^ *:-+ *$/', $s)) + $attr[$n] = $this->_doTable_makeAlignAttr('left'); + else + $attr[$n] = ''; + } + + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $head = $this->parseSpan($head); + $headers = preg_split('/ *[|] */', $head); + $col_count = count($headers); + $attr = array_pad($attr, $col_count, ''); + + # Write column headers. + $text = "\n"; + $text .= "\n"; + $text .= "\n"; + foreach ($headers as $n => $header) + $text .= " ".$this->runSpanGamut(trim($header))."\n"; + $text .= "\n"; + $text .= "\n"; + + # Split content by row. + $rows = explode("\n", trim($content, "\n")); + + $text .= "\n"; + foreach ($rows as $row) { + # Parsing span elements, including code spans, character escapes, + # and inline HTML tags, so that pipes inside those gets ignored. + $row = $this->parseSpan($row); + + # Split row by cell. + $row_cells = preg_split('/ *[|] */', $row, $col_count); + $row_cells = array_pad($row_cells, $col_count, ''); + + $text .= "\n"; + foreach ($row_cells as $n => $cell) + $text .= " ".$this->runSpanGamut(trim($cell))."\n"; + $text .= "\n"; + } + $text .= "\n"; + $text .= "
    "; + + return $this->hashBlock($text) . "\n"; + } + + + protected function doDefLists($text) { + # + # Form HTML definition lists. + # + $less_than_tab = $this->tab_width - 1; + + # Re-usable pattern to match any entire dl list: + $whole_list_re = '(?> + ( # $1 = whole list + ( # $2 + [ ]{0,'.$less_than_tab.'} + ((?>.*\S.*\n)+) # $3 = defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?s:.+?) + ( # $4 + \z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another term + [ ]{0,'.$less_than_tab.'} + (?: \S.*\n )+? # defined term + \n? + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + (?! # Negative lookahead for another definition + [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition + ) + ) + ) + )'; // mx + + $text = preg_replace_callback('{ + (?>\A\n?|(?<=\n\n)) + '.$whole_list_re.' + }mx', + array(&$this, '_doDefLists_callback'), $text); + + return $text; + } + protected function _doDefLists_callback($matches) { + # Re-usable patterns to match list item bullets and number markers: + $list = $matches[1]; + + # Turn double returns into triple returns, so that we can make a + # paragraph for the last item in a list, if necessary: + $result = trim($this->processDefListItems($list)); + $result = "
    \n" . $result . "\n
    "; + return $this->hashBlock($result) . "\n\n"; + } + + + protected function processDefListItems($list_str) { + # + # Process the contents of a single definition list, splitting it + # into individual term and definition list items. + # + $less_than_tab = $this->tab_width - 1; + + # trim trailing blank lines: + $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); + + # Process definition terms. + $list_str = preg_replace_callback('{ + (?>\A\n?|\n\n+) # leading line + ( # definition terms = $1 + [ ]{0,'.$less_than_tab.'} # leading whitespace + (?!\:[ ]|[ ]) # negative lookahead for a definition + # mark (colon) or more whitespace. + (?> \S.* \n)+? # actual term (not whitespace). + ) + (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed + # with a definition mark. + }xm', + array(&$this, '_processDefListItems_callback_dt'), $list_str); + + # Process actual definitions. + $list_str = preg_replace_callback('{ + \n(\n+)? # leading line = $1 + ( # marker space = $2 + [ ]{0,'.$less_than_tab.'} # whitespace before colon + \:[ ]+ # definition mark (colon) + ) + ((?s:.+?)) # definition text = $3 + (?= \n+ # stop at next definition mark, + (?: # next term or end of text + [ ]{0,'.$less_than_tab.'} \:[ ] | +
    | \z + ) + ) + }xm', + array(&$this, '_processDefListItems_callback_dd'), $list_str); + + return $list_str; + } + protected function _processDefListItems_callback_dt($matches) { + $terms = explode("\n", trim($matches[1])); + $text = ''; + foreach ($terms as $term) { + $term = $this->runSpanGamut(trim($term)); + $text .= "\n
    " . $term . "
    "; + } + return $text . "\n"; + } + protected function _processDefListItems_callback_dd($matches) { + $leading_line = $matches[1]; + $marker_space = $matches[2]; + $def = $matches[3]; + + if ($leading_line || preg_match('/\n{2,}/', $def)) { + # Replace marker with the appropriate whitespace indentation + $def = str_repeat(' ', strlen($marker_space)) . $def; + $def = $this->runBlockGamut($this->outdent($def . "\n\n")); + $def = "\n". $def ."\n"; + } + else { + $def = rtrim($def); + $def = $this->runSpanGamut($this->outdent($def)); + } + + return "\n
    " . $def . "
    \n"; + } + + + protected function doFencedCodeBlocks($text) { + # + # Adding the fenced code block syntax to regular Markdown: + # + # ~~~ + # Code block + # ~~~ + # + $less_than_tab = $this->tab_width; + + $text = preg_replace_callback('{ + (?:\n|\A) + # 1: Opening marker + ( + (?:~{3,}|`{3,}) # 3 or more tildes/backticks. + ) + [ ]* + (?: + \.?([-_:a-zA-Z0-9]+) # 2: standalone class name + | + '.$this->id_class_attr_catch_re.' # 3: Extra attributes + )? + [ ]* \n # Whitespace and newline following marker. + + # 4: Content + ( + (?> + (?!\1 [ ]* \n) # Not a closing marker. + .*\n+ + )+ + ) + + # Closing marker. + \1 [ ]* (?= \n ) + }xm', + array(&$this, '_doFencedCodeBlocks_callback'), $text); + + return $text; + } + protected function _doFencedCodeBlocks_callback($matches) { + $classname =& $matches[2]; + $attrs =& $matches[3]; + $codeblock = $matches[4]; + $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); + $codeblock = preg_replace_callback('/^\n+/', + array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); + + if ($classname != "") { + if ($classname{0} == '.') + $classname = substr($classname, 1); + $attr_str = ' class="'.$this->code_class_prefix.$classname.'"'; + } else { + $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs); + } + $pre_attr_str = $this->code_attr_on_pre ? $attr_str : ''; + $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str; + $codeblock = "$codeblock
    "; + + return "\n\n".$this->hashBlock($codeblock)."\n\n"; + } + protected function _doFencedCodeBlocks_newlines($matches) { + return str_repeat("empty_element_suffix", + strlen($matches[0])); + } + + + # + # Redefining emphasis markers so that emphasis by underscore does not + # work in the middle of a word. + # + protected $em_relist = array( + '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags + # + # Strip leading and trailing lines: + $text = preg_replace('/\A\n+|\n+\z/', '', $text); + + $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); + + # + # Wrap

    tags and unhashify HTML blocks + # + foreach ($grafs as $key => $value) { + $value = trim($this->runSpanGamut($value)); + + # Check if this should be enclosed in a paragraph. + # Clean tag hashes & block tag hashes are left alone. + $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); + + if ($is_p) { + $value = "

    $value

    "; + } + $grafs[$key] = $value; + } + + # Join grafs in one text, then unhash HTML tags. + $text = implode("\n\n", $grafs); + + # Finish by removing any tag hashes still present in $text. + $text = $this->unhash($text); + + return $text; + } + + + ### Footnotes + + protected function stripFootnotes($text) { + # + # Strips link definitions from text, stores the URLs and titles in + # hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [^id]: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1 + [ ]* + \n? # maybe *one* newline + ( # text = $2 (no blank lines allowed) + (?: + .+ # actual text + | + \n # newlines but + (?!\[\^.+?\]:\s)# negative lookahead for footnote marker. + (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed + # by non-indented content + )* + ) + }xm', + array(&$this, '_stripFootnotes_callback'), + $text); + return $text; + } + protected function _stripFootnotes_callback($matches) { + $note_id = $this->fn_id_prefix . $matches[1]; + $this->footnotes[$note_id] = $this->outdent($matches[2]); + return ''; # String that will replace the block + } + + + protected function doFootnotes($text) { + # + # Replace footnote references in $text [^id] with a special text-token + # which will be replaced by the actual footnote marker in appendFootnotes. + # + if (!$this->in_anchor) { + $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); + } + return $text; + } + + + protected function appendFootnotes($text) { + # + # Append footnote list to text. + # + $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $text); + + if (!empty($this->footnotes_ordered)) { + $text .= "\n\n"; + $text .= "
    \n"; + $text .= "empty_element_suffix ."\n"; + $text .= "
      \n\n"; + + $attr = ""; + if ($this->fn_backlink_class != "") { + $class = $this->fn_backlink_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_backlink_title != "") { + $title = $this->fn_backlink_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + $num = 0; + + while (!empty($this->footnotes_ordered)) { + $footnote = reset($this->footnotes_ordered); + $note_id = key($this->footnotes_ordered); + unset($this->footnotes_ordered[$note_id]); + $ref_count = $this->footnotes_ref_count[$note_id]; + unset($this->footnotes_ref_count[$note_id]); + unset($this->footnotes[$note_id]); + + $footnote .= "\n"; # Need to append newline before parsing. + $footnote = $this->runBlockGamut("$footnote\n"); + $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', + array(&$this, '_appendFootnotes_callback'), $footnote); + + $attr = str_replace("%%", ++$num, $attr); + $note_id = $this->encodeAttribute($note_id); + + # Prepare backlink, multiple backlinks if multiple references + $backlink = ""; + for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) { + $backlink .= " "; + } + # Add backlink to last paragraph; create new paragraph if needed. + if (preg_match('{

      $}', $footnote)) { + $footnote = substr($footnote, 0, -4) . " $backlink

      "; + } else { + $footnote .= "\n\n

      $backlink

      "; + } + + $text .= "
    1. \n"; + $text .= $footnote . "\n"; + $text .= "
    2. \n\n"; + } + + $text .= "
    \n"; + $text .= "
    "; + } + return $text; + } + protected function _appendFootnotes_callback($matches) { + $node_id = $this->fn_id_prefix . $matches[1]; + + # Create footnote marker only if it has a corresponding footnote *and* + # the footnote hasn't been used by another marker. + if (isset($this->footnotes[$node_id])) { + $num =& $this->footnotes_numbers[$node_id]; + if (!isset($num)) { + # Transfer footnote content to the ordered list and give it its + # number + $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id]; + $this->footnotes_ref_count[$node_id] = 1; + $num = $this->footnote_counter++; + $ref_count_mark = ''; + } else { + $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1; + } + + $attr = ""; + if ($this->fn_link_class != "") { + $class = $this->fn_link_class; + $class = $this->encodeAttribute($class); + $attr .= " class=\"$class\""; + } + if ($this->fn_link_title != "") { + $title = $this->fn_link_title; + $title = $this->encodeAttribute($title); + $attr .= " title=\"$title\""; + } + + $attr = str_replace("%%", $num, $attr); + $node_id = $this->encodeAttribute($node_id); + + return + "". + "$num". + ""; + } + + return "[^".$matches[1]."]"; + } + + + ### Abbreviations ### + + protected function stripAbbreviations($text) { + # + # Strips abbreviations from text, stores titles in hash references. + # + $less_than_tab = $this->tab_width - 1; + + # Link defs are in the form: [id]*: url "optional title" + $text = preg_replace_callback('{ + ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 + (.*) # text = $2 (no blank lines allowed) + }xm', + array(&$this, '_stripAbbreviations_callback'), + $text); + return $text; + } + protected function _stripAbbreviations_callback($matches) { + $abbr_word = $matches[1]; + $abbr_desc = $matches[2]; + if ($this->abbr_word_re) + $this->abbr_word_re .= '|'; + $this->abbr_word_re .= preg_quote($abbr_word); + $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); + return ''; # String that will replace the block + } + + + protected function doAbbreviations($text) { + # + # Find defined abbreviations in text and wrap them in elements. + # + if ($this->abbr_word_re) { + // cannot use the /x modifier because abbr_word_re may + // contain significant spaces: + $text = preg_replace_callback('{'. + '(?abbr_word_re.')'. + '(?![\w\x1A])'. + '}', + array(&$this, '_doAbbreviations_callback'), $text); + } + return $text; + } + protected function _doAbbreviations_callback($matches) { + $abbr = $matches[0]; + if (isset($this->abbr_desciptions[$abbr])) { + $desc = $this->abbr_desciptions[$abbr]; + if (empty($desc)) { + return $this->hashPart("$abbr"); + } else { + $desc = $this->encodeAttribute($desc); + return $this->hashPart("$abbr"); + } + } else { + return $matches[0]; + } + } + +} diff --git a/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php b/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php new file mode 100644 index 000000000..e11b1ef97 --- /dev/null +++ b/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php @@ -0,0 +1,11 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# +namespace Michelf; + + +# Just force Michelf/Markdown.php to load. This is needed to load +# the temporary implementation class. See below for details. +\Michelf\Markdown::MARKDOWNLIB_VERSION; + +# +# Markdown Extra Parser Class +# +# Note: Currently the implementation resides in the temporary class +# \Michelf\MarkdownExtra_TmpImpl (in the same file as \Michelf\Markdown). +# This makes it easier to propagate the changes between the three different +# packaging styles of PHP Markdown. Once this issue is resolved, the +# _MarkdownExtra_TmpImpl will disappear and this one will contain the code. +# + +class MarkdownExtra extends \Michelf\_MarkdownExtra_TmpImpl { + + ### Parser Implementation ### + + # Temporarily, the implemenation is in the _MarkdownExtra_TmpImpl class. + # See note above. + +} + diff --git a/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php b/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php new file mode 100644 index 000000000..a023ed4e3 --- /dev/null +++ b/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php @@ -0,0 +1,9 @@ + +# +# Original Markdown +# Copyright (c) 2004-2006 John Gruber +# +# +namespace Michelf; + + +# +# Markdown Parser Interface +# + +interface MarkdownInterface { + + # + # Initialize the parser and return the result of its transform method. + # This will work fine for derived classes too. + # + public static function defaultTransform($text); + + # + # Main function. Performs some preprocessing on the input text + # and pass it through the document gamut. + # + public function transform($text); + +} + + +?> \ No newline at end of file diff --git a/core/lib/michelf/php-markdown/Readme.md b/core/lib/michelf/php-markdown/Readme.md new file mode 100644 index 000000000..25b0cdc0d --- /dev/null +++ b/core/lib/michelf/php-markdown/Readme.md @@ -0,0 +1,271 @@ +PHP Markdown +============ + +PHP Markdown Lib 1.4.0 - 29 Nov 2013 + +by Michel Fortin + + +based on Markdown by John Gruber + + + +Introduction +------------ + +This is a library package that includes the PHP Markdown parser and its +sibling PHP Markdown Extra with additional features. + +Markdown is a text-to-HTML conversion tool for web writers. Markdown +allows you to write using an easy-to-read, easy-to-write plain text +format, then convert it to structurally valid XHTML (or HTML). + +"Markdown" is actually two things: a plain text markup syntax, and a +software tool, originally written in Perl, that converts the plain text +markup to HTML. PHP Markdown is a port to PHP of the original Markdown +program by John Gruber. + +* [Full documentation of the Markdown syntax]() + - Daring Fireball (John Gruber) +* [Markdown Extra syntax additions]() + - Michel Fortin + + +Requirement +----------- + +This library package requires PHP 5.3 or later. + +Note: The older plugin/library hybrid package for PHP Markdown and +PHP Markdown Extra is still maintained and will work with PHP 4.0.5 and later. + +Before PHP 5.3.7, pcre.backtrack_limit defaults to 100 000, which is too small +in many situations. You might need to set it to higher values. Later PHP +releases defaults to 1 000 000, which is usually fine. + + +Usage +----- + +This library package is meant to be used with class autoloading. For autoloading +to work, your project needs have setup a PSR-0-compatible autoloader. See the +included Readme.php file for a minimal autoloader setup. (If you cannot use +autoloading, see below.) + +With class autoloading in place, putting the 'Michelf' folder in your +include path should be enough for this to work: + + use \Michelf\Markdown; + $my_html = Markdown::defaultTransform($my_text); + +Markdown Extra syntax is also available the same way: + + use \Michelf\MarkdownExtra; + $my_html = MarkdownExtra::defaultTransform($my_text); + +If you wish to use PHP Markdown with another text filter function +built to parse HTML, you should filter the text *after* the `transform` +function call. This is an example with [PHP SmartyPants][psp]: + + use \Michelf\Markdown, \Michelf\SmartyPants; + $my_html = Markdown::defaultTransform($my_text); + $my_html = SmartyPants::defaultTransform($my_html); + +All these examples are using the static `defaultTransform` static function +found inside the parser class. If you want to customize the parser +configuration, you can also instantiate it directly and change some +configuration variables: + + use \Michelf\MarkdownExtra; + $parser = new MarkdownExtra; + $parser->fn_id_prefix = "post22-"; + $my_html = $parser->transform($my_text); + +To learn more, see the full list of [configuration variables]. + + [configuration variables]: http://michelf.ca/projects/php-markdown/configuration/ + + +### Usage without an autoloader + +If you cannot use class autoloading, you can still use `include` or `require` +to access the parser. To load the `\Michelf\Markdown` parser, do it this way: + + require_once 'Michelf/Markdown.inc.php'; + +Or, if you need the `\Michelf\MarkdownExtra` parser: + + require_once 'Michelf/MarkdownExtra.inc.php'; + +While the plain `.php` files depend on autoloading to work correctly, using the +`.inc.php` files instead will eagerly load the dependencies that would be +loaded on demand if you were using autoloading. + + +Public API and Versioning Policy +--------------------------------- + +Version numbers are of the form *major*.*minor*.*patch*. + +The public API of PHP Markdown consist of the two parser classes `Markdown` +and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform` +functions and their configuration variables. The public API is stable for +a given major version number. It might get additions when the minor version +number increments. + +**Protected members are not considered public API.** This is unconventional +and deserves an explanation. Incrementing the major version number every time +the underlying implementation of something changes is going to give +nonessential version numbers for the vast majority of people who just use the +parser. Protected members are meant to create parser subclasses that behave in +different ways. Very few people create parser subclasses. I don't want to +discourage it by making everything private, but at the same time I can't +guarantee any stable hook between versions if you use protected members. + +**Syntax changes** will increment the minor number for new features, and the +patch number for small corrections. A *new feature* is something that needs a +change in the syntax documentation. Note that since PHP Markdown Lib includes +two parsers, a syntax change for either of them will increment the minor +number. Also note that there is nothing perfectly backward-compatible with the +Markdown syntax: all inputs are always valid, so new features always replace +something that was previously legal, although generally nonsensical to do. + + +Bugs +---- + +To file bug reports please send email to: + + +Please include with your report: (1) the example input; (2) the output you +expected; (3) the output PHP Markdown actually produced. + +If you have a problem where Markdown gives you an empty result, first check +that the backtrack limit is not too low by running `php --info | grep pcre`. +See Installation and Requirement above for details. + + +Development and Testing +----------------------- + +Pull requests for fixing bugs are welcome. Proposed new features are +going meticulously reviewed -- taking into account backward compatibility, +potential side effects, and future extensibility -- before deciding on +acceptance or rejection. + +If you make a pull request that includes changes to the parser please add +tests for what is being changed to [MDTest][] and make a pull request there +too. + + [MDTest]: https://github.com/michelf/mdtest/ + + +Version History +--------------- + +PHP Markdown Lib 1.4.0 (29 Nov 2013) + +* Added support for the `tel:` URL scheme in automatic links. + + + + It gets converted to this (note the `tel:` prefix becomes invisible): + + +1-111-111-1111 + +* Added backtick fenced code blocks to MarkdownExtra, originally from + Github-Flavored Markdown. + +* Added an interface called MarkdownInterface interface implemented by both + the Markdown and MarkdownExtra parsers. You can use the interface if + you want to create a mockup parser object for unit testing. + +* For those of you who cannot use class autoloading, you can now + include `Michelf/Markdown.inc.php` or `Michelf/MarkdownExtra.inc.php` (note + the `.inc.php` extension) to automatically include other files required + by the parser. + + +PHP Markdown Lib 1.3 (11 Apr 2013) + +This is the first release of PHP Markdown Lib. This package requires PHP +version 5.3 or later and is designed to work with PSR-0 autoloading and, +optionally with Composer. Here is a list of the changes since +PHP Markdown Extra 1.2.6: + +* Plugin interface for WordPress and other systems is no longer present in + the Lib package. The classic package is still available if you need it: + + +* Added `public` and `protected` protection attributes, plus a section about + what is "public API" and what isn't in the Readme file. + +* Changed HTML output for footnotes: now instead of adding `rel` and `rev` + attributes, footnotes links have the class name `footnote-ref` and + backlinks `footnote-backref`. + +* Fixed some regular expressions to make PCRE not shout warnings about POSIX + collation classes (dependent on your version of PCRE). + +* Added optional class and id attributes to images and links using the same + syntax as for headers: + + [link](url){#id .class} + ![img](url){#id .class} + + It work too for reference-style links and images. In this case you need + to put those attributes at the reference definition: + + [link][linkref] or [linkref] + ![img][linkref] + + [linkref]: url "optional title" {#id .class} + +* Fixed a PHP notice message triggered when some table column separator + markers are missing on the separator line below column headers. + +* Fixed a small mistake that could cause the parser to retain an invalid + state related to parsing links across multiple runs. This was never + observed (that I know of), but it's still worth fixing. + + +Copyright and License +--------------------- + +PHP Markdown Lib +Copyright (c) 2004-2013 Michel Fortin + +All rights reserved. + +Based on Markdown +Copyright (c) 2003-2005 John Gruber + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +* Neither the name "Markdown" nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as +is" and any express or implied warranties, including, but not limited +to, the implied warranties of merchantability and fitness for a +particular purpose are disclaimed. In no event shall the copyright owner +or contributors be liable for any direct, indirect, incidental, special, +exemplary, or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or +profits; or business interruption) however caused and on any theory of +liability, whether in contract, strict liability, or tort (including +negligence or otherwise) arising in any way out of the use of this +software, even if advised of the possibility of such damage. diff --git a/core/lib/michelf/php-markdown/Readme.php b/core/lib/michelf/php-markdown/Readme.php new file mode 100644 index 000000000..d007b119f --- /dev/null +++ b/core/lib/michelf/php-markdown/Readme.php @@ -0,0 +1,31 @@ + + + + + PHP Markdown Lib - Readme + + + + + diff --git a/core/lib/michelf/php-markdown/composer.json b/core/lib/michelf/php-markdown/composer.json new file mode 100644 index 000000000..45abc6774 --- /dev/null +++ b/core/lib/michelf/php-markdown/composer.json @@ -0,0 +1,31 @@ +{ + "name": "michelf/php-markdown", + "type": "library", + "description": "PHP Markdown", + "homepage": "http://michelf.ca/projects/php-markdown/", + "keywords": ["markdown"], + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "http://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "http://daringfireball.net/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-0": { "Michelf": "" } + }, + "extra": { + "branch-alias": { + "dev-lib": "1.4.x-dev" + } + } +} From bff54a1f91391226c21d57ddc8c41c7b9383f2ad Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:47:11 -0400 Subject: [PATCH 070/166] adding yaml support --- .../yaml/Symfony/Component/Yaml/.gitignore | 3 + .../yaml/Symfony/Component/Yaml/CHANGELOG.md | 8 + .../yaml/Symfony/Component/Yaml/Dumper.php | 73 + .../yaml/Symfony/Component/Yaml/Escaper.php | 89 + .../Yaml/Exception/DumpException.php | 23 + .../Yaml/Exception/ExceptionInterface.php | 23 + .../Yaml/Exception/ParseException.php | 148 ++ .../Yaml/Exception/RuntimeException.php | 23 + .../yaml/Symfony/Component/Yaml/Inline.php | 469 +++++ .../yaml/Symfony/Component/Yaml/LICENSE | 19 + .../yaml/Symfony/Component/Yaml/Parser.php | 638 +++++++ .../yaml/Symfony/Component/Yaml/README.md | 19 + .../Component/Yaml/Tests/DumperTest.php | 207 ++ .../Yaml/Tests/Fixtures/YtsAnchorAlias.yml | 31 + .../Yaml/Tests/Fixtures/YtsBasicTests.yml | 178 ++ .../Yaml/Tests/Fixtures/YtsBlockMapping.yml | 51 + .../Tests/Fixtures/YtsDocumentSeparator.yml | 85 + .../Yaml/Tests/Fixtures/YtsErrorTests.yml | 25 + .../Tests/Fixtures/YtsFlowCollections.yml | 60 + .../Yaml/Tests/Fixtures/YtsFoldedScalars.yml | 176 ++ .../Tests/Fixtures/YtsNullsAndEmpties.yml | 45 + .../Fixtures/YtsSpecificationExamples.yml | 1695 +++++++++++++++++ .../Yaml/Tests/Fixtures/YtsTypeTransfers.yml | 244 +++ .../Yaml/Tests/Fixtures/embededPhp.yml | 1 + .../Yaml/Tests/Fixtures/escapedCharacters.yml | 147 ++ .../Component/Yaml/Tests/Fixtures/index.yml | 18 + .../Yaml/Tests/Fixtures/sfComments.yml | 65 + .../Yaml/Tests/Fixtures/sfCompact.yml | 159 ++ .../Yaml/Tests/Fixtures/sfMergeKey.yml | 27 + .../Yaml/Tests/Fixtures/sfObjects.yml | 11 + .../Yaml/Tests/Fixtures/sfQuotes.yml | 33 + .../Component/Yaml/Tests/Fixtures/sfTests.yml | 135 ++ .../Tests/Fixtures/unindentedCollections.yml | 62 + .../Component/Yaml/Tests/InlineTest.php | 231 +++ .../Yaml/Tests/ParseExceptionTest.php | 30 + .../Component/Yaml/Tests/ParserTest.php | 619 ++++++ .../Symfony/Component/Yaml/Tests/YamlTest.php | 31 + .../yaml/Symfony/Component/Yaml/Unescaper.php | 146 ++ .../yaml/Symfony/Component/Yaml/Yaml.php | 100 + .../yaml/Symfony/Component/Yaml/composer.json | 31 + .../Symfony/Component/Yaml/phpunit.xml.dist | 29 + 41 files changed, 6207 insertions(+) create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/README.md create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json create mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore b/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md b/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md new file mode 100644 index 000000000..096cf654d --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md @@ -0,0 +1,8 @@ +CHANGELOG +========= + +2.1.0 +----- + + * Yaml::parse() does not evaluate loaded files as PHP files by default + anymore (call Yaml::enablePhpParsing() to get back the old behavior) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php new file mode 100644 index 000000000..26103c88e --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Dumper dumps PHP variables to YAML strings. + * + * @author Fabien Potencier + */ +class Dumper +{ + /** + * The amount of spaces to use for indentation of nested nodes. + * + * @var int + */ + protected $indentation = 4; + + /** + * Sets the indentation. + * + * @param int $num The amount of spaces to use for indentation of nested nodes. + */ + public function setIndentation($num) + { + $this->indentation = (int) $num; + } + + /** + * Dumps a PHP value to YAML. + * + * @param mixed $input The PHP value + * @param int $inline The level where you switch to inline YAML + * @param int $indent The level of indentation (used internally) + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML representation of the PHP value + */ + public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false) + { + $output = ''; + $prefix = $indent ? str_repeat(' ', $indent) : ''; + + if ($inline <= 0 || !is_array($input) || empty($input)) { + $output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); + } else { + $isAHash = array_keys($input) !== range(0, count($input) - 1); + + foreach ($input as $key => $value) { + $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + + $output .= sprintf('%s%s%s%s', + $prefix, + $isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-', + $willBeInlined ? ' ' : "\n", + $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport) + ).($willBeInlined ? "\n" : ''); + } + } + + return $output; + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php new file mode 100644 index 000000000..4a6b62161 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Escaper encapsulates escaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + */ +class Escaper +{ + // Characters that would cause a dumped string to require double quoting. + const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; + + // Mapping arrays for escaping a double quoted string. The backslash is + // first to ensure proper escaping because str_replace operates iteratively + // on the input arrays. This ordering of the characters avoids the use of strtr, + // which performs more slowly. + private static $escapees = array('\\\\', '\\"', '"', + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"); + private static $escaped = array('\\"', '\\\\', '\\"', + "\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", + "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f", + "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", + "\\x18", "\\x19", "\\x1a", "\\e", "\\x1c", "\\x1d", "\\x1e", "\\x1f", + "\\N", "\\_", "\\L", "\\P"); + + /** + * Determines if a PHP value would require double quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require double quotes. + */ + public static function requiresDoubleQuoting($value) + { + return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); + } + + /** + * Escapes and surrounds a PHP value with double quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithDoubleQuotes($value) + { + return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); + } + + /** + * Determines if a PHP value would require single quoting in YAML. + * + * @param string $value A PHP value + * + * @return bool True if the value would require single quotes. + */ + public static function requiresSingleQuoting($value) + { + return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); + } + + /** + * Escapes and surrounds a PHP value with single quotes. + * + * @param string $value A PHP value + * + * @return string The quoted, escaped string + */ + public static function escapeWithSingleQuotes($value) + { + return sprintf("'%s'", str_replace('\'', '\'\'', $value)); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php new file mode 100644 index 000000000..9b3e6de07 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during dumping. + * + * @author Fabien Potencier + * + * @api + */ +class DumpException extends RuntimeException +{ +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php new file mode 100644 index 000000000..92e5c2ea4 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Fabien Potencier + * + * @api + */ +interface ExceptionInterface +{ +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php new file mode 100644 index 000000000..ff01d6b9a --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php @@ -0,0 +1,148 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +if (!defined('JSON_UNESCAPED_UNICODE')) { + define('JSON_UNESCAPED_SLASHES', 64); + define('JSON_UNESCAPED_UNICODE', 256); +} + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Fabien Potencier + * + * @api + */ +class ParseException extends RuntimeException +{ + private $parsedFile; + private $parsedLine; + private $snippet; + private $rawMessage; + + /** + * Constructor. + * + * @param string $message The error message + * @param int $parsedLine The line where the error occurred + * @param int $snippet The snippet of code near the problem + * @param string $parsedFile The file name where the error occurred + * @param \Exception $previous The previous exception + */ + public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) + { + $this->parsedFile = $parsedFile; + $this->parsedLine = $parsedLine; + $this->snippet = $snippet; + $this->rawMessage = $message; + + $this->updateRepr(); + + parent::__construct($this->message, 0, $previous); + } + + /** + * Gets the snippet of code near the error. + * + * @return string The snippet of code + */ + public function getSnippet() + { + return $this->snippet; + } + + /** + * Sets the snippet of code near the error. + * + * @param string $snippet The code snippet + */ + public function setSnippet($snippet) + { + $this->snippet = $snippet; + + $this->updateRepr(); + } + + /** + * Gets the filename where the error occurred. + * + * This method returns null if a string is parsed. + * + * @return string The filename + */ + public function getParsedFile() + { + return $this->parsedFile; + } + + /** + * Sets the filename where the error occurred. + * + * @param string $parsedFile The filename + */ + public function setParsedFile($parsedFile) + { + $this->parsedFile = $parsedFile; + + $this->updateRepr(); + } + + /** + * Gets the line where the error occurred. + * + * @return int The file line + */ + public function getParsedLine() + { + return $this->parsedLine; + } + + /** + * Sets the line where the error occurred. + * + * @param int $parsedLine The file line + */ + public function setParsedLine($parsedLine) + { + $this->parsedLine = $parsedLine; + + $this->updateRepr(); + } + + private function updateRepr() + { + $this->message = $this->rawMessage; + + $dot = false; + if ('.' === substr($this->message, -1)) { + $this->message = substr($this->message, 0, -1); + $dot = true; + } + + if (null !== $this->parsedFile) { + $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); + } + + if ($this->parsedLine >= 0) { + $this->message .= sprintf(' at line %d', $this->parsedLine); + } + + if ($this->snippet) { + $this->message .= sprintf(' (near "%s")', $this->snippet); + } + + if ($dot) { + $this->message .= '.'; + } + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php new file mode 100644 index 000000000..3573bf15a --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Exception; + +/** + * Exception class thrown when an error occurs during parsing. + * + * @author Romain Neutron + * + * @api + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php new file mode 100644 index 000000000..b0d6a031f --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php @@ -0,0 +1,469 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Exception\DumpException; + +/** + * Inline implements a YAML parser/dumper for the YAML inline syntax. + * + * @author Fabien Potencier + */ +class Inline +{ + const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; + + private static $exceptionOnInvalidType = false; + private static $objectSupport = false; + + /** + * Converts a YAML string to a PHP array. + * + * @param string $value A YAML string + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return array A PHP array representing the YAML string + * + * @throws ParseException + */ + public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false) + { + self::$exceptionOnInvalidType = $exceptionOnInvalidType; + self::$objectSupport = $objectSupport; + + $value = trim($value); + + if (0 == strlen($value)) { + return ''; + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('ASCII'); + } + + $i = 0; + switch ($value[0]) { + case '[': + $result = self::parseSequence($value, $i); + ++$i; + break; + case '{': + $result = self::parseMapping($value, $i); + ++$i; + break; + default: + $result = self::parseScalar($value, null, array('"', "'"), $i); + } + + // some comments are allowed at the end + if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { + throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $result; + } + + /** + * Dumps a given PHP variable to a YAML string. + * + * @param mixed $value The PHP variable to convert + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML string representing the PHP array + * + * @throws DumpException When trying to dump PHP resource + */ + public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false) + { + switch (true) { + case is_resource($value): + if ($exceptionOnInvalidType) { + throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); + } + + return 'null'; + case is_object($value): + if ($objectSupport) { + return '!!php/object:'.serialize($value); + } + + if ($exceptionOnInvalidType) { + throw new DumpException('Object support when dumping a YAML file has been disabled.'); + } + + return 'null'; + case is_array($value): + return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport); + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case ctype_digit($value): + return is_string($value) ? "'$value'" : (int) $value; + case is_numeric($value): + $locale = setlocale(LC_NUMERIC, 0); + if (false !== $locale) { + setlocale(LC_NUMERIC, 'C'); + } + $repr = is_string($value) ? "'$value'" : (is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : strval($value)); + + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + + return $repr; + case Escaper::requiresDoubleQuoting($value): + return Escaper::escapeWithDoubleQuotes($value); + case Escaper::requiresSingleQuoting($value): + return Escaper::escapeWithSingleQuotes($value); + case '' == $value: + return "''"; + case preg_match(self::getTimestampRegex(), $value): + case in_array(strtolower($value), array('null', '~', 'true', 'false')): + return "'$value'"; + default: + return $value; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * @param array $value The PHP array to dump + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string The YAML string representing the PHP array + */ + private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) + { + // array + $keys = array_keys($value); + if ((1 == count($keys) && '0' == $keys[0]) + || (count($keys) > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2) + ) { + $output = array(); + foreach ($value as $val) { + $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); + } + + return sprintf('[%s]', implode(', ', $output)); + } + + // mapping + $output = array(); + foreach ($value as $key => $val) { + $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); + } + + return sprintf('{ %s }', implode(', ', $output)); + } + + /** + * Parses a scalar to a YAML string. + * + * @param scalar $scalar + * @param string $delimiters + * @param array $stringDelimiters + * @param int &$i + * @param bool $evaluate + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true) + { + if (in_array($scalar[$i], $stringDelimiters)) { + // quoted scalar + $output = self::parseQuotedScalar($scalar, $i); + + if (null !== $delimiters) { + $tmp = ltrim(substr($scalar, $i), ' '); + if (!in_array($tmp[0], $delimiters)) { + throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); + } + } + } else { + // "normal" string + if (!$delimiters) { + $output = substr($scalar, $i); + $i += strlen($output); + + // remove comments + if (false !== $strpos = strpos($output, ' #')) { + $output = rtrim(substr($output, 0, $strpos)); + } + } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { + $output = $match[1]; + $i += strlen($output); + } else { + throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar)); + } + + if ($evaluate) { + $output = self::evaluateScalar($output); + } + } + + return $output; + } + + /** + * Parses a quoted scalar to YAML. + * + * @param string $scalar + * @param int &$i + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseQuotedScalar($scalar, &$i) + { + if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { + throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); + } + + $output = substr($match[0], 1, strlen($match[0]) - 2); + + $unescaper = new Unescaper(); + if ('"' == $scalar[$i]) { + $output = $unescaper->unescapeDoubleQuotedString($output); + } else { + $output = $unescaper->unescapeSingleQuotedString($output); + } + + $i += strlen($match[0]); + + return $output; + } + + /** + * Parses a sequence to a YAML string. + * + * @param string $sequence + * @param int &$i + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseSequence($sequence, &$i = 0) + { + $output = array(); + $len = strlen($sequence); + $i += 1; + + // [foo, bar, ...] + while ($i < $len) { + switch ($sequence[$i]) { + case '[': + // nested sequence + $output[] = self::parseSequence($sequence, $i); + break; + case '{': + // nested mapping + $output[] = self::parseMapping($sequence, $i); + break; + case ']': + return $output; + case ',': + case ' ': + break; + default: + $isQuoted = in_array($sequence[$i], array('"', "'")); + $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i); + + if (!$isQuoted && false !== strpos($value, ': ')) { + // embedded mapping? + try { + $value = self::parseMapping('{'.$value.'}'); + } catch (\InvalidArgumentException $e) { + // no, it's not + } + } + + $output[] = $value; + + --$i; + } + + ++$i; + } + + throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence)); + } + + /** + * Parses a mapping to a YAML string. + * + * @param string $mapping + * @param int &$i + * + * @return string A YAML string + * + * @throws ParseException When malformed inline YAML string is parsed + */ + private static function parseMapping($mapping, &$i = 0) + { + $output = array(); + $len = strlen($mapping); + $i += 1; + + // {foo: bar, bar:foo, ...} + while ($i < $len) { + switch ($mapping[$i]) { + case ' ': + case ',': + ++$i; + continue 2; + case '}': + return $output; + } + + // key + $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); + + // value + $done = false; + while ($i < $len) { + switch ($mapping[$i]) { + case '[': + // nested sequence + $output[$key] = self::parseSequence($mapping, $i); + $done = true; + break; + case '{': + // nested mapping + $output[$key] = self::parseMapping($mapping, $i); + $done = true; + break; + case ':': + case ' ': + break; + default: + $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); + $done = true; + --$i; + } + + ++$i; + + if ($done) { + continue 2; + } + } + } + + throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping)); + } + + /** + * Evaluates scalars and replaces magic values. + * + * @param string $scalar + * + * @return string A YAML string + */ + private static function evaluateScalar($scalar) + { + $scalar = trim($scalar); + $scalarLower = strtolower($scalar); + switch (true) { + case 'null' === $scalarLower: + case '' === $scalar: + case '~' === $scalar: + return; + case 'true' === $scalarLower: + return true; + case 'false' === $scalarLower: + return false; + // Optimise for returning strings. + case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]): + switch (true) { + case 0 === strpos($scalar, '!str'): + return (string) substr($scalar, 5); + case 0 === strpos($scalar, '! '): + return intval(self::parseScalar(substr($scalar, 2))); + case 0 === strpos($scalar, '!!php/object:'): + if (self::$objectSupport) { + return unserialize(substr($scalar, 13)); + } + + if (self::$exceptionOnInvalidType) { + throw new ParseException('Object support when parsing a YAML file has been disabled.'); + } + + return; + case ctype_digit($scalar): + $raw = $scalar; + $cast = intval($scalar); + + return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): + $raw = $scalar; + $cast = intval($scalar); + + return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); + case is_numeric($scalar): + return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar); + case '.inf' === $scalarLower: + case '.nan' === $scalarLower: + return -log(0); + case '-.inf' === $scalarLower: + return log(0); + case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): + return floatval(str_replace(',', '', $scalar)); + case preg_match(self::getTimestampRegex(), $scalar): + return strtotime($scalar); + } + default: + return (string) $scalar; + } + } + + /** + * Gets a regex that matches a YAML date. + * + * @return string The regular expression + * + * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 + */ + private static function getTimestampRegex() + { + return <<[0-9][0-9][0-9][0-9]) + -(?P[0-9][0-9]?) + -(?P[0-9][0-9]?) + (?:(?:[Tt]|[ \t]+) + (?P[0-9][0-9]?) + :(?P[0-9][0-9]) + :(?P[0-9][0-9]) + (?:\.(?P[0-9]*))? + (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) + (?::(?P[0-9][0-9]))?))?)? + $~x +EOF; + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE b/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php new file mode 100644 index 000000000..d4e0a9fa6 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php @@ -0,0 +1,638 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Parser parses YAML strings to convert them to PHP arrays. + * + * @author Fabien Potencier + */ +class Parser +{ + const FOLDED_SCALAR_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; + + private $offset = 0; + private $lines = array(); + private $currentLineNb = -1; + private $currentLine = ''; + private $refs = array(); + + /** + * Constructor + * + * @param int $offset The offset of YAML document (used for line numbers in error messages) + */ + public function __construct($offset = 0) + { + $this->offset = $offset; + } + + /** + * Parses a YAML string to a PHP value. + * + * @param string $value A YAML string + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return mixed A PHP value + * + * @throws ParseException If the YAML is not valid + */ + public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false) + { + $this->currentLineNb = -1; + $this->currentLine = ''; + $this->lines = explode("\n", $this->cleanup($value)); + + if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) { + throw new ParseException('The YAML value does not appear to be valid UTF-8.'); + } + + if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { + $mbEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + } + + $data = array(); + $context = null; + while ($this->moveToNextLine()) { + if ($this->isCurrentLineEmpty()) { + continue; + } + + // tab? + if ("\t" === $this->currentLine[0]) { + throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + $isRef = $isInPlace = $isProcessed = false; + if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { + if ($context && 'mapping' == $context) { + throw new ParseException('You cannot define a sequence item when in a mapping'); + } + $context = 'sequence'; + + if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + // array + if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new Parser($c); + $parser->refs =& $this->refs; + $data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); + } else { + if (isset($values['leadspaces']) + && ' ' == $values['leadspaces'] + && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) + ) { + // this is a compact notation element, add to next block and parse + $c = $this->getRealCurrentLineNb(); + $parser = new Parser($c); + $parser->refs =& $this->refs; + + $block = $values['value']; + if ($this->isNextLineIndented()) { + $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2); + } + + $data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport); + } else { + $data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); + } + } + } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && false === strpos($values['key'],' #')) { + if ($context && 'sequence' == $context) { + throw new ParseException('You cannot define a mapping item when in a sequence'); + } + $context = 'mapping'; + + // force correct settings + Inline::parse(null, $exceptionOnInvalidType, $objectSupport); + try { + $key = Inline::parseScalar($values['key']); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if ('<<' === $key) { + if (isset($values['value']) && 0 === strpos($values['value'], '*')) { + $isInPlace = substr($values['value'], 1); + if (!array_key_exists($isInPlace, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } else { + if (isset($values['value']) && $values['value'] !== '') { + $value = $values['value']; + } else { + $value = $this->getNextEmbedBlock(); + } + $c = $this->getRealCurrentLineNb() + 1; + $parser = new Parser($c); + $parser->refs =& $this->refs; + $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport); + + $merged = array(); + if (!is_array($parsed)) { + throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } elseif (isset($parsed[0])) { + // Numeric array, merge individual elements + foreach (array_reverse($parsed) as $parsedItem) { + if (!is_array($parsedItem)) { + throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); + } + $merged = array_merge($parsedItem, $merged); + } + } else { + // Associative array, merge + $merged = array_merge($merged, $parsed); + } + + $isProcessed = $merged; + } + } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { + $isRef = $matches['ref']; + $values['value'] = $matches['value']; + } + + if ($isProcessed) { + // Merge keys + $data = $isProcessed; + // hash + } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { + // if next line is less indented or equal, then it means that the current value is null + if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { + $data[$key] = null; + } else { + $c = $this->getRealCurrentLineNb() + 1; + $parser = new Parser($c); + $parser->refs =& $this->refs; + $data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); + } + } else { + if ($isInPlace) { + $data = $this->refs[$isInPlace]; + } else { + $data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); + } + } + } else { + // 1-liner optionally followed by newline + $lineCount = count($this->lines); + if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) { + try { + $value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + + if (is_array($value)) { + $first = reset($value); + if (is_string($first) && 0 === strpos($first, '*')) { + $data = array(); + foreach ($value as $alias) { + $data[] = $this->refs[substr($alias, 1)]; + } + $value = $data; + } + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return $value; + } + + switch (preg_last_error()) { + case PREG_INTERNAL_ERROR: + $error = 'Internal PCRE error.'; + break; + case PREG_BACKTRACK_LIMIT_ERROR: + $error = 'pcre.backtrack_limit reached.'; + break; + case PREG_RECURSION_LIMIT_ERROR: + $error = 'pcre.recursion_limit reached.'; + break; + case PREG_BAD_UTF8_ERROR: + $error = 'Malformed UTF-8 data.'; + break; + case PREG_BAD_UTF8_OFFSET_ERROR: + $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; + break; + default: + $error = 'Unable to parse.'; + } + + throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + + if ($isRef) { + $this->refs[$isRef] = end($data); + } + } + + if (isset($mbEncoding)) { + mb_internal_encoding($mbEncoding); + } + + return empty($data) ? null : $data; + } + + /** + * Returns the current line number (takes the offset into account). + * + * @return int The current line number + */ + private function getRealCurrentLineNb() + { + return $this->currentLineNb + $this->offset; + } + + /** + * Returns the current line indentation. + * + * @return int The current line indentation + */ + private function getCurrentLineIndentation() + { + return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); + } + + /** + * Returns the next embed block of YAML. + * + * @param int $indentation The indent level at which the block is to be read, or null for default + * + * @return string A YAML string + * + * @throws ParseException When indentation problem are detected + */ + private function getNextEmbedBlock($indentation = null) + { + $this->moveToNextLine(); + + if (null === $indentation) { + $newIndent = $this->getCurrentLineIndentation(); + + $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem($this->currentLine); + + if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } else { + $newIndent = $indentation; + } + + $data = array(substr($this->currentLine, $newIndent)); + + $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine); + + // Comments must not be removed inside a string block (ie. after a line ending with "|") + $removeCommentsPattern = '~'.self::FOLDED_SCALAR_PATTERN.'$~'; + $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); + + while ($this->moveToNextLine()) { + $indent = $this->getCurrentLineIndentation(); + + if ($indent === $newIndent) { + $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); + } + + if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem($this->currentLine)) { + $this->moveToPreviousLine(); + break; + } + + if ($this->isCurrentLineBlank()) { + $data[] = substr($this->currentLine, $newIndent); + continue; + } + + if ($removeComments && $this->isCurrentLineComment()) { + continue; + } + + if ($indent >= $newIndent) { + $data[] = substr($this->currentLine, $newIndent); + } elseif (0 == $indent) { + $this->moveToPreviousLine(); + + break; + } else { + throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); + } + } + + return implode("\n", $data); + } + + /** + * Moves the parser to the next line. + * + * @return bool + */ + private function moveToNextLine() + { + if ($this->currentLineNb >= count($this->lines) - 1) { + return false; + } + + $this->currentLine = $this->lines[++$this->currentLineNb]; + + return true; + } + + /** + * Moves the parser to the previous line. + */ + private function moveToPreviousLine() + { + $this->currentLine = $this->lines[--$this->currentLineNb]; + } + + /** + * Parses a YAML value. + * + * @param string $value A YAML value + * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise + * @param bool $objectSupport True if object support is enabled, false otherwise + * + * @return mixed A PHP value + * + * @throws ParseException When reference does not exist + */ + private function parseValue($value, $exceptionOnInvalidType, $objectSupport) + { + if (0 === strpos($value, '*')) { + if (false !== $pos = strpos($value, '#')) { + $value = substr($value, 1, $pos - 2); + } else { + $value = substr($value, 1); + } + + if (!array_key_exists($value, $this->refs)) { + throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); + } + + return $this->refs[$value]; + } + + if (preg_match('/^'.self::FOLDED_SCALAR_PATTERN.'$/', $value, $matches)) { + $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; + + return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers))); + } + + try { + return Inline::parse($value, $exceptionOnInvalidType, $objectSupport); + } catch (ParseException $e) { + $e->setParsedLine($this->getRealCurrentLineNb() + 1); + $e->setSnippet($this->currentLine); + + throw $e; + } + } + + /** + * Parses a folded scalar. + * + * @param string $separator The separator that was used to begin this folded scalar (| or >) + * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) + * @param int $indentation The indentation that was used to begin this folded scalar + * + * @return string The text value + */ + private function parseFoldedScalar($separator, $indicator = '', $indentation = 0) + { + $notEOF = $this->moveToNextLine(); + if (!$notEOF) { + return ''; + } + + $isCurrentLineBlank = $this->isCurrentLineBlank(); + $text = ''; + + // leading blank lines are consumed before determining indentation + while ($notEOF && $isCurrentLineBlank) { + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $text .= "\n"; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + + // determine indentation if not specified + if (0 === $indentation) { + if (preg_match('/^ +/', $this->currentLine, $matches)) { + $indentation = strlen($matches[0]); + } + } + + if ($indentation > 0) { + $pattern = sprintf('/^ {%d}(.*)$/', $indentation); + + while ( + $notEOF && ( + $isCurrentLineBlank || + preg_match($pattern, $this->currentLine, $matches) + ) + ) { + if ($isCurrentLineBlank) { + $text .= substr($this->currentLine, $indentation); + } else { + $text .= $matches[1]; + } + + // newline only if not EOF + if ($notEOF = $this->moveToNextLine()) { + $text .= "\n"; + $isCurrentLineBlank = $this->isCurrentLineBlank(); + } + } + } elseif ($notEOF) { + $text .= "\n"; + } + + if ($notEOF) { + $this->moveToPreviousLine(); + } + + // replace all non-trailing single newlines with spaces in folded blocks + if ('>' === $separator) { + preg_match('/(\n*)$/', $text, $matches); + $text = preg_replace('/(?getCurrentLineIndentation(); + $EOF = !$this->moveToNextLine(); + + while (!$EOF && $this->isCurrentLineEmpty()) { + $EOF = !$this->moveToNextLine(); + } + + if ($EOF) { + return false; + } + + $ret = false; + if ($this->getCurrentLineIndentation() > $currentIndentation) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the current line is blank or if it is a comment line. + * + * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise + */ + private function isCurrentLineEmpty() + { + return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); + } + + /** + * Returns true if the current line is blank. + * + * @return bool Returns true if the current line is blank, false otherwise + */ + private function isCurrentLineBlank() + { + return '' == trim($this->currentLine, ' '); + } + + /** + * Returns true if the current line is a comment line. + * + * @return bool Returns true if the current line is a comment line, false otherwise + */ + private function isCurrentLineComment() + { + //checking explicitly the first char of the trim is faster than loops or strpos + $ltrimmedLine = ltrim($this->currentLine, ' '); + + return $ltrimmedLine[0] === '#'; + } + + /** + * Cleanups a YAML string to be parsed. + * + * @param string $value The input YAML string + * + * @return string A cleaned up YAML string + */ + private function cleanup($value) + { + $value = str_replace(array("\r\n", "\r"), "\n", $value); + + // strip YAML header + $count = 0; + $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count); + $this->offset += $count; + + // remove leading comments + $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + } + + // remove start of the document marker (---) + $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); + if ($count == 1) { + // items have been removed, update the offset + $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); + $value = $trimmedValue; + + // remove end of the document marker (...) + $value = preg_replace('#\.\.\.\s*$#s', '', $value); + } + + return $value; + } + + /** + * Returns true if the next line starts unindented collection + * + * @return bool Returns true if the next line starts unindented collection, false otherwise + */ + private function isNextLineUnIndentedCollection() + { + $currentIndentation = $this->getCurrentLineIndentation(); + $notEOF = $this->moveToNextLine(); + + while ($notEOF && $this->isCurrentLineEmpty()) { + $notEOF = $this->moveToNextLine(); + } + + if (false === $notEOF) { + return false; + } + + $ret = false; + if ( + $this->getCurrentLineIndentation() == $currentIndentation + && + $this->isStringUnIndentedCollectionItem($this->currentLine) + ) { + $ret = true; + } + + $this->moveToPreviousLine(); + + return $ret; + } + + /** + * Returns true if the string is un-indented collection item + * + * @return bool Returns true if the string is un-indented collection item, false otherwise + */ + private function isStringUnIndentedCollectionItem() + { + return (0 === strpos($this->currentLine, '- ')); + } + +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md b/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md new file mode 100644 index 000000000..941a3460e --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md @@ -0,0 +1,19 @@ +Yaml Component +============== + +YAML implements most of the YAML 1.2 specification. + + use Symfony\Component\Yaml\Yaml; + + $array = Yaml::parse($file); + + print Yaml::dump($array); + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Yaml/ + $ composer.phar install + $ phpunit diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php new file mode 100644 index 000000000..ec2c65e3d --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Yaml; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Dumper; + +class DumperTest extends \PHPUnit_Framework_TestCase +{ + protected $parser; + protected $dumper; + protected $path; + + protected $array = array( + '' => 'bar', + 'foo' => '#bar', + 'foo\'bar' => array(), + 'bar' => array(1, 'foo'), + 'foobar' => array( + 'foo' => 'bar', + 'bar' => array(1, 'foo'), + 'foobar' => array( + 'foo' => 'bar', + 'bar' => array(1, 'foo'), + ), + ), + ); + + protected function setUp() + { + $this->parser = new Parser(); + $this->dumper = new Dumper(); + $this->path = __DIR__.'/Fixtures'; + } + + protected function tearDown() + { + $this->parser = null; + $this->dumper = null; + $this->path = null; + $this->array = null; + } + + public function testSetIndentation() + { + $this->dumper->setIndentation(7); + +$expected = <<assertEquals($expected, $this->dumper->dump($this->array, 4, 0)); + } + + public function testSpecifications() + { + $files = $this->parser->parse(file_get_contents($this->path.'/index.yml')); + foreach ($files as $file) { + $yamls = file_get_contents($this->path.'/'.$file.'.yml'); + + // split YAMLs documents + foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { + if (!$yaml) { + continue; + } + + $test = $this->parser->parse($yaml); + if (isset($test['dump_skip']) && $test['dump_skip']) { + continue; + } elseif (isset($test['todo']) && $test['todo']) { + // TODO + } else { + eval('$expected = '.trim($test['php']).';'); + + $this->assertEquals($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']); + } + } + } + } + + public function testInlineLevel() + { + $expected = <<assertEquals($expected, $this->dumper->dump($this->array, -10), '->dump() takes an inline level argument'); +$this->assertEquals($expected, $this->dumper->dump($this->array, 0), '->dump() takes an inline level argument'); + +$expected = <<assertEquals($expected, $this->dumper->dump($this->array, 1), '->dump() takes an inline level argument'); + + $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 2), '->dump() takes an inline level argument'); + + $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 3), '->dump() takes an inline level argument'); + + $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 4), '->dump() takes an inline level argument'); + $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument'); + } + + public function testObjectSupportEnabled() + { + $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); + + $this->assertEquals('{ foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects'); + } + + public function testObjectSupportDisabledButNoExceptions() + { + $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1)); + + $this->assertEquals('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled'); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\DumpException + */ + public function testObjectSupportDisabledWithExceptions() + { + $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true, false); + } +} + +class A +{ + public $a = 'foo'; +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml new file mode 100644 index 000000000..5f9c94275 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml @@ -0,0 +1,31 @@ +--- %YAML:1.0 +test: Simple Alias Example +brief: > + If you need to refer to the same item of data twice, + you can give that item an alias. The alias is a plain + string, starting with an ampersand. The item may then + be referred to by the alias throughout your document + by using an asterisk before the name of the alias. + This is called an anchor. +yaml: | + - &showell Steve + - Clark + - Brian + - Oren + - *showell +php: | + array('Steve', 'Clark', 'Brian', 'Oren', 'Steve') + +--- +test: Alias of a Mapping +brief: > + An alias can be used on any item of data, including + sequences, mappings, and other complex data types. +yaml: | + - &hello + Meat: pork + Starch: potato + - banana + - *hello +php: | + array(array('Meat'=>'pork', 'Starch'=>'potato'), 'banana', array('Meat'=>'pork', 'Starch'=>'potato')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml new file mode 100644 index 000000000..ac0efa88d --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml @@ -0,0 +1,178 @@ +--- %YAML:1.0 +test: Simple Sequence +brief: | + You can specify a list in YAML by placing each + member of the list on a new line with an opening + dash. These lists are called sequences. +yaml: | + - apple + - banana + - carrot +php: | + array('apple', 'banana', 'carrot') +--- +test: Nested Sequences +brief: | + You can include a sequence within another + sequence by giving the sequence an empty + dash, followed by an indented list. +yaml: | + - + - foo + - bar + - baz +php: | + array(array('foo', 'bar', 'baz')) +--- +test: Mixed Sequences +brief: | + Sequences can contain any YAML data, + including strings and other sequences. +yaml: | + - apple + - + - foo + - bar + - x123 + - banana + - carrot +php: | + array('apple', array('foo', 'bar', 'x123'), 'banana', 'carrot') +--- +test: Deeply Nested Sequences +brief: | + Sequences can be nested even deeper, with each + level of indentation representing a level of + depth. +yaml: | + - + - + - uno + - dos +php: | + array(array(array('uno', 'dos'))) +--- +test: Simple Mapping +brief: | + You can add a keyed list (also known as a dictionary or + hash) to your document by placing each member of the + list on a new line, with a colon separating the key + from its value. In YAML, this type of list is called + a mapping. +yaml: | + foo: whatever + bar: stuff +php: | + array('foo' => 'whatever', 'bar' => 'stuff') +--- +test: Sequence in a Mapping +brief: | + A value in a mapping can be a sequence. +yaml: | + foo: whatever + bar: + - uno + - dos +php: | + array('foo' => 'whatever', 'bar' => array('uno', 'dos')) +--- +test: Nested Mappings +brief: | + A value in a mapping can be another mapping. +yaml: | + foo: whatever + bar: + fruit: apple + name: steve + sport: baseball +php: | + array( + 'foo' => 'whatever', + 'bar' => array( + 'fruit' => 'apple', + 'name' => 'steve', + 'sport' => 'baseball' + ) + ) +--- +test: Mixed Mapping +brief: | + A mapping can contain any assortment + of mappings and sequences as values. +yaml: | + foo: whatever + bar: + - + fruit: apple + name: steve + sport: baseball + - more + - + python: rocks + perl: papers + ruby: scissorses +php: | + array( + 'foo' => 'whatever', + 'bar' => array( + array( + 'fruit' => 'apple', + 'name' => 'steve', + 'sport' => 'baseball' + ), + 'more', + array( + 'python' => 'rocks', + 'perl' => 'papers', + 'ruby' => 'scissorses' + ) + ) + ) +--- +test: Mapping-in-Sequence Shortcut +todo: true +brief: | + If you are adding a mapping to a sequence, you + can place the mapping on the same line as the + dash as a shortcut. +yaml: | + - work on YAML.py: + - work on Store +php: | + array(array('work on YAML.py' => array('work on Store'))) +--- +test: Sequence-in-Mapping Shortcut +todo: true +brief: | + The dash in a sequence counts as indentation, so + you can add a sequence inside of a mapping without + needing spaces as indentation. +yaml: | + allow: + - 'localhost' + - '%.sourceforge.net' + - '%.freepan.org' +php: | + array('allow' => array('localhost', '%.sourceforge.net', '%.freepan.org')) +--- +todo: true +test: Merge key +brief: | + A merge key ('<<') can be used in a mapping to insert other mappings. If + the value associated with the merge key is a mapping, each of its key/value + pairs is inserted into the current mapping. +yaml: | + mapping: + name: Joe + job: Accountant + <<: + age: 38 +php: | + array( + 'mapping' => + array( + 'name' => 'Joe', + 'job' => 'Accountant', + 'age' => 38 + ) + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml new file mode 100644 index 000000000..f7ca469b4 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml @@ -0,0 +1,51 @@ +--- +test: One Element Mapping +brief: | + A mapping with one key/value pair +yaml: | + foo: bar +php: | + array('foo' => 'bar') +--- +test: Multi Element Mapping +brief: | + More than one key/value pair +yaml: | + red: baron + white: walls + blue: berries +php: | + array( + 'red' => 'baron', + 'white' => 'walls', + 'blue' => 'berries', + ) +--- +test: Values aligned +brief: | + Often times human editors of documents will align the values even + though YAML emitters generally don't. +yaml: | + red: baron + white: walls + blue: berries +php: | + array( + 'red' => 'baron', + 'white' => 'walls', + 'blue' => 'berries', + ) +--- +test: Colons aligned +brief: | + Spaces can come before the ': ' key/value separator. +yaml: | + red : baron + white : walls + blue : berries +php: | + array( + 'red' => 'baron', + 'white' => 'walls', + 'blue' => 'berries', + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml new file mode 100644 index 000000000..f8501ddc2 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml @@ -0,0 +1,85 @@ +--- %YAML:1.0 +test: Trailing Document Separator +todo: true +brief: > + You can separate YAML documents + with a string of three dashes. +yaml: | + - foo: 1 + bar: 2 + --- + more: stuff +python: | + [ + [ { 'foo': 1, 'bar': 2 } ], + { 'more': 'stuff' } + ] +ruby: | + [ { 'foo' => 1, 'bar' => 2 } ] + +--- +test: Leading Document Separator +todo: true +brief: > + You can explicity give an opening + document separator to your YAML stream. +yaml: | + --- + - foo: 1 + bar: 2 + --- + more: stuff +python: | + [ + [ {'foo': 1, 'bar': 2}], + {'more': 'stuff'} + ] +ruby: | + [ { 'foo' => 1, 'bar' => 2 } ] + +--- +test: YAML Header +todo: true +brief: > + The opening separator can contain directives + to the YAML parser, such as the version + number. +yaml: | + --- %YAML:1.0 + foo: 1 + bar: 2 +php: | + array('foo' => 1, 'bar' => 2) +documents: 1 + +--- +test: Red Herring Document Separator +brief: > + Separators included in blocks or strings + are treated as blocks or strings, as the + document separator should have no indentation + preceding it. +yaml: | + foo: | + --- +php: | + array('foo' => "---\n") + +--- +test: Multiple Document Separators in Block +brief: > + This technique allows you to embed other YAML + documents within literal blocks. +yaml: | + foo: | + --- + foo: bar + --- + yo: baz + bar: | + fooness +php: | + array( + 'foo' => "---\nfoo: bar\n---\nyo: baz\n", + 'bar' => "fooness\n" + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml new file mode 100644 index 000000000..e8506fcb6 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml @@ -0,0 +1,25 @@ +--- +test: Missing value for hash item +todo: true +brief: | + Third item in this hash doesn't have a value +yaml: | + okay: value + also okay: ~ + causes error because no value specified + last key: value okay here too +python-error: causes error because no value specified + +--- +test: Not indenting enough +brief: | + There was a bug in PyYaml where it was off by one + in the indentation check. It was allowing the YAML + below. +# This is actually valid YAML now. Someone should tell showell. +yaml: | + foo: + firstline: 1 + secondline: 2 +php: | + array('foo' => null, 'firstline' => 1, 'secondline' => 2) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml new file mode 100644 index 000000000..03090e4ab --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml @@ -0,0 +1,60 @@ +--- +test: Simple Inline Array +brief: > + Sequences can be contained on a + single line, using the inline syntax. + Separate each entry with commas and + enclose in square brackets. +yaml: | + seq: [ a, b, c ] +php: | + array('seq' => array('a', 'b', 'c')) +--- +test: Simple Inline Hash +brief: > + Mapping can also be contained on + a single line, using the inline + syntax. Each key-value pair is + separated by a colon, with a comma + between each entry in the mapping. + Enclose with curly braces. +yaml: | + hash: { name: Steve, foo: bar } +php: | + array('hash' => array('name' => 'Steve', 'foo' => 'bar')) +--- +test: Multi-line Inline Collections +todo: true +brief: > + Both inline sequences and inline mappings + can span multiple lines, provided that you + indent the additional lines. +yaml: | + languages: [ Ruby, + Perl, + Python ] + websites: { YAML: yaml.org, + Ruby: ruby-lang.org, + Python: python.org, + Perl: use.perl.org } +php: | + array( + 'languages' => array('Ruby', 'Perl', 'Python'), + 'websites' => array( + 'YAML' => 'yaml.org', + 'Ruby' => 'ruby-lang.org', + 'Python' => 'python.org', + 'Perl' => 'use.perl.org' + ) + ) +--- +test: Commas in Values (not in the spec!) +todo: true +brief: > + List items in collections are delimited by commas, but + there must be a space after each comma. This allows you + to add numbers without quoting. +yaml: | + attendances: [ 45,123, 70,000, 17,222 ] +php: | + array('attendances' => array(45123, 70000, 17222)) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml new file mode 100644 index 000000000..a14735a55 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml @@ -0,0 +1,176 @@ +--- %YAML:1.0 +test: Single ending newline +brief: > + A pipe character, followed by an indented + block of text is treated as a literal + block, in which newlines are preserved + throughout the block, including the final + newline. +yaml: | + --- + this: | + Foo + Bar +php: | + array('this' => "Foo\nBar\n") +--- +test: The '+' indicator +brief: > + The '+' indicator says to keep newlines at the end of text + blocks. +yaml: | + normal: | + extra new lines not kept + + preserving: |+ + extra new lines are kept + + + dummy: value +php: | + array( + 'normal' => "extra new lines not kept\n", + 'preserving' => "extra new lines are kept\n\n\n", + 'dummy' => 'value' + ) +--- +test: Three trailing newlines in literals +brief: > + To give you more control over how space + is preserved in text blocks, YAML has + the keep '+' and chomp '-' indicators. + The keep indicator will preserve all + ending newlines, while the chomp indicator + will strip all ending newlines. +yaml: | + clipped: | + This has one newline. + + + + same as "clipped" above: "This has one newline.\n" + + stripped: |- + This has no newline. + + + + same as "stripped" above: "This has no newline." + + kept: |+ + This has four newlines. + + + + same as "kept" above: "This has four newlines.\n\n\n\n" +php: | + array( + 'clipped' => "This has one newline.\n", + 'same as "clipped" above' => "This has one newline.\n", + 'stripped' => 'This has no newline.', + 'same as "stripped" above' => 'This has no newline.', + 'kept' => "This has four newlines.\n\n\n\n", + 'same as "kept" above' => "This has four newlines.\n\n\n\n" + ) +--- +test: Extra trailing newlines with spaces +todo: true +brief: > + Normally, only a single newline is kept + from the end of a literal block, unless the + keep '+' character is used in combination + with the pipe. The following example + will preserve all ending whitespace + since the last line of both literal blocks + contains spaces which extend past the indentation + level. +yaml: | + --- + this: | + Foo + + + kept: |+ + Foo + + +php: | + array('this' => "Foo\n\n \n", + 'kept' => "Foo\n\n \n" ) + +--- +test: Folded Block in a Sequence +brief: > + A greater-then character, followed by an indented + block of text is treated as a folded block, in + which lines of text separated by a single newline + are concatenated as a single line. +yaml: | + --- + - apple + - banana + - > + can't you see + the beauty of yaml? + hmm + - dog +php: | + array( + 'apple', + 'banana', + "can't you see the beauty of yaml? hmm\n", + 'dog' + ) +--- +test: Folded Block as a Mapping Value +brief: > + Both literal and folded blocks can be + used in collections, as values in a + sequence or a mapping. +yaml: | + --- + quote: > + Mark McGwire's + year was crippled + by a knee injury. + source: espn +php: | + array( + 'quote' => "Mark McGwire's year was crippled by a knee injury.\n", + 'source' => 'espn' + ) +--- +test: Three trailing newlines in folded blocks +brief: > + The keep and chomp indicators can also + be applied to folded blocks. +yaml: | + clipped: > + This has one newline. + + + + same as "clipped" above: "This has one newline.\n" + + stripped: >- + This has no newline. + + + + same as "stripped" above: "This has no newline." + + kept: >+ + This has four newlines. + + + + same as "kept" above: "This has four newlines.\n\n\n\n" +php: | + array( + 'clipped' => "This has one newline.\n", + 'same as "clipped" above' => "This has one newline.\n", + 'stripped' => 'This has no newline.', + 'same as "stripped" above' => 'This has no newline.', + 'kept' => "This has four newlines.\n\n\n\n", + 'same as "kept" above' => "This has four newlines.\n\n\n\n" + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml new file mode 100644 index 000000000..9a5300f2e --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml @@ -0,0 +1,45 @@ +--- %YAML:1.0 +test: Empty Sequence +brief: > + You can represent the empty sequence + with an empty inline sequence. +yaml: | + empty: [] +php: | + array('empty' => array()) +--- +test: Empty Mapping +brief: > + You can represent the empty mapping + with an empty inline mapping. +yaml: | + empty: {} +php: | + array('empty' => array()) +--- +test: Empty Sequence as Entire Document +yaml: | + [] +php: | + array() +--- +test: Empty Mapping as Entire Document +yaml: | + {} +php: | + array() +--- +test: Null as Document +yaml: | + ~ +php: | + null +--- +test: Empty String +brief: > + You can represent an empty string + with a pair of quotes. +yaml: | + '' +php: | + '' diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml new file mode 100644 index 000000000..1e59f3bf9 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml @@ -0,0 +1,1695 @@ +--- %YAML:1.0 +test: Sequence of scalars +spec: 2.1 +yaml: | + - Mark McGwire + - Sammy Sosa + - Ken Griffey +php: | + array('Mark McGwire', 'Sammy Sosa', 'Ken Griffey') +--- +test: Mapping of scalars to scalars +spec: 2.2 +yaml: | + hr: 65 + avg: 0.278 + rbi: 147 +php: | + array('hr' => 65, 'avg' => 0.278, 'rbi' => 147) +--- +test: Mapping of scalars to sequences +spec: 2.3 +yaml: | + american: + - Boston Red Sox + - Detroit Tigers + - New York Yankees + national: + - New York Mets + - Chicago Cubs + - Atlanta Braves +php: | + array('american' => + array( 'Boston Red Sox', 'Detroit Tigers', + 'New York Yankees' ), + 'national' => + array( 'New York Mets', 'Chicago Cubs', + 'Atlanta Braves' ) + ) +--- +test: Sequence of mappings +spec: 2.4 +yaml: | + - + name: Mark McGwire + hr: 65 + avg: 0.278 + - + name: Sammy Sosa + hr: 63 + avg: 0.288 +php: | + array( + array('name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278), + array('name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288) + ) +--- +test: Legacy A5 +todo: true +spec: legacy_A5 +yaml: | + ? + - New York Yankees + - Atlanta Braves + : + - 2001-07-02 + - 2001-08-12 + - 2001-08-14 + ? + - Detroit Tigers + - Chicago Cubs + : + - 2001-07-23 +perl-busted: > + YAML.pm will be able to emulate this behavior soon. In this regard + it may be somewhat more correct than Python's native behaviour which + can only use tuples as mapping keys. PyYAML will also need to figure + out some clever way to roundtrip structured keys. +python: | + [ + { + ('New York Yankees', 'Atlanta Braves'): + [yaml.timestamp('2001-07-02'), + yaml.timestamp('2001-08-12'), + yaml.timestamp('2001-08-14')], + ('Detroit Tigers', 'Chicago Cubs'): + [yaml.timestamp('2001-07-23')] + } + ] +ruby: | + { + [ 'New York Yankees', 'Atlanta Braves' ] => + [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ], + [ 'Detroit Tigers', 'Chicago Cubs' ] => + [ Date.new( 2001, 7, 23 ) ] + } +syck: | + struct test_node seq1[] = { + { T_STR, 0, "New York Yankees" }, + { T_STR, 0, "Atlanta Braves" }, + end_node + }; + struct test_node seq2[] = { + { T_STR, 0, "2001-07-02" }, + { T_STR, 0, "2001-08-12" }, + { T_STR, 0, "2001-08-14" }, + end_node + }; + struct test_node seq3[] = { + { T_STR, 0, "Detroit Tigers" }, + { T_STR, 0, "Chicago Cubs" }, + end_node + }; + struct test_node seq4[] = { + { T_STR, 0, "2001-07-23" }, + end_node + }; + struct test_node map[] = { + { T_SEQ, 0, 0, seq1 }, + { T_SEQ, 0, 0, seq2 }, + { T_SEQ, 0, 0, seq3 }, + { T_SEQ, 0, 0, seq4 }, + end_node + }; + struct test_node stream[] = { + { T_MAP, 0, 0, map }, + end_node + }; + +--- +test: Sequence of sequences +spec: 2.5 +yaml: | + - [ name , hr , avg ] + - [ Mark McGwire , 65 , 0.278 ] + - [ Sammy Sosa , 63 , 0.288 ] +php: | + array( + array( 'name', 'hr', 'avg' ), + array( 'Mark McGwire', 65, 0.278 ), + array( 'Sammy Sosa', 63, 0.288 ) + ) +--- +test: Mapping of mappings +todo: true +spec: 2.6 +yaml: | + Mark McGwire: {hr: 65, avg: 0.278} + Sammy Sosa: { + hr: 63, + avg: 0.288 + } +php: | + array( + 'Mark McGwire' => + array( 'hr' => 65, 'avg' => 0.278 ), + 'Sammy Sosa' => + array( 'hr' => 63, 'avg' => 0.288 ) + ) +--- +test: Two documents in a stream each with a leading comment +todo: true +spec: 2.7 +yaml: | + # Ranking of 1998 home runs + --- + - Mark McGwire + - Sammy Sosa + - Ken Griffey + + # Team ranking + --- + - Chicago Cubs + - St Louis Cardinals +ruby: | + y = YAML::Stream.new + y.add( [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] ) + y.add( [ 'Chicago Cubs', 'St Louis Cardinals' ] ) +documents: 2 + +--- +test: Play by play feed from a game +todo: true +spec: 2.8 +yaml: | + --- + time: 20:03:20 + player: Sammy Sosa + action: strike (miss) + ... + --- + time: 20:03:47 + player: Sammy Sosa + action: grand slam + ... +perl: | + [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] +documents: 2 + +--- +test: Single document with two comments +spec: 2.9 +yaml: | + hr: # 1998 hr ranking + - Mark McGwire + - Sammy Sosa + rbi: + # 1998 rbi ranking + - Sammy Sosa + - Ken Griffey +php: | + array( + 'hr' => array( 'Mark McGwire', 'Sammy Sosa' ), + 'rbi' => array( 'Sammy Sosa', 'Ken Griffey' ) + ) +--- +test: Node for Sammy Sosa appears twice in this document +spec: 2.10 +yaml: | + --- + hr: + - Mark McGwire + # Following node labeled SS + - &SS Sammy Sosa + rbi: + - *SS # Subsequent occurrence + - Ken Griffey +php: | + array( + 'hr' => + array('Mark McGwire', 'Sammy Sosa'), + 'rbi' => + array('Sammy Sosa', 'Ken Griffey') + ) +--- +test: Mapping between sequences +todo: true +spec: 2.11 +yaml: | + ? # PLAY SCHEDULE + - Detroit Tigers + - Chicago Cubs + : + - 2001-07-23 + + ? [ New York Yankees, + Atlanta Braves ] + : [ 2001-07-02, 2001-08-12, + 2001-08-14 ] +ruby: | + { + [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ], + [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] + } +syck: | + struct test_node seq1[] = { + { T_STR, 0, "New York Yankees" }, + { T_STR, 0, "Atlanta Braves" }, + end_node + }; + struct test_node seq2[] = { + { T_STR, 0, "2001-07-02" }, + { T_STR, 0, "2001-08-12" }, + { T_STR, 0, "2001-08-14" }, + end_node + }; + struct test_node seq3[] = { + { T_STR, 0, "Detroit Tigers" }, + { T_STR, 0, "Chicago Cubs" }, + end_node + }; + struct test_node seq4[] = { + { T_STR, 0, "2001-07-23" }, + end_node + }; + struct test_node map[] = { + { T_SEQ, 0, 0, seq3 }, + { T_SEQ, 0, 0, seq4 }, + { T_SEQ, 0, 0, seq1 }, + { T_SEQ, 0, 0, seq2 }, + end_node + }; + struct test_node stream[] = { + { T_MAP, 0, 0, map }, + end_node + }; + +--- +test: Sequence key shortcut +spec: 2.12 +yaml: | + --- + # products purchased + - item : Super Hoop + quantity: 1 + - item : Basketball + quantity: 4 + - item : Big Shoes + quantity: 1 +php: | + array ( + array ( + 'item' => 'Super Hoop', + 'quantity' => 1, + ), + array ( + 'item' => 'Basketball', + 'quantity' => 4, + ), + array ( + 'item' => 'Big Shoes', + 'quantity' => 1, + ) + ) +perl: | + [ + { item => 'Super Hoop', quantity => 1 }, + { item => 'Basketball', quantity => 4 }, + { item => 'Big Shoes', quantity => 1 } + ] + +ruby: | + [ + { 'item' => 'Super Hoop', 'quantity' => 1 }, + { 'item' => 'Basketball', 'quantity' => 4 }, + { 'item' => 'Big Shoes', 'quantity' => 1 } + ] +python: | + [ + { 'item': 'Super Hoop', 'quantity': 1 }, + { 'item': 'Basketball', 'quantity': 4 }, + { 'item': 'Big Shoes', 'quantity': 1 } + ] +syck: | + struct test_node map1[] = { + { T_STR, 0, "item" }, + { T_STR, 0, "Super Hoop" }, + { T_STR, 0, "quantity" }, + { T_STR, 0, "1" }, + end_node + }; + struct test_node map2[] = { + { T_STR, 0, "item" }, + { T_STR, 0, "Basketball" }, + { T_STR, 0, "quantity" }, + { T_STR, 0, "4" }, + end_node + }; + struct test_node map3[] = { + { T_STR, 0, "item" }, + { T_STR, 0, "Big Shoes" }, + { T_STR, 0, "quantity" }, + { T_STR, 0, "1" }, + end_node + }; + struct test_node seq[] = { + { T_MAP, 0, 0, map1 }, + { T_MAP, 0, 0, map2 }, + { T_MAP, 0, 0, map3 }, + end_node + }; + struct test_node stream[] = { + { T_SEQ, 0, 0, seq }, + end_node + }; + + +--- +test: Literal perserves newlines +todo: true +spec: 2.13 +yaml: | + # ASCII Art + --- | + \//||\/|| + // || ||_ +perl: | + "\\//||\\/||\n// || ||_\n" +ruby: | + "\\//||\\/||\n// || ||_\n" +python: | + [ + flushLeft( + """ + \//||\/|| + // || ||_ + """ + ) + ] +syck: | + struct test_node stream[] = { + { T_STR, 0, "\\//||\\/||\n// || ||_\n" }, + end_node + }; + +--- +test: Folded treats newlines as a space +todo: true +spec: 2.14 +yaml: | + --- + Mark McGwire's + year was crippled + by a knee injury. +perl: | + "Mark McGwire's year was crippled by a knee injury." +ruby: | + "Mark McGwire's year was crippled by a knee injury." +python: | + [ "Mark McGwire's year was crippled by a knee injury." ] +syck: | + struct test_node stream[] = { + { T_STR, 0, "Mark McGwire's year was crippled by a knee injury." }, + end_node + }; + +--- +test: Newlines preserved for indented and blank lines +todo: true +spec: 2.15 +yaml: | + --- > + Sammy Sosa completed another + fine season with great stats. + + 63 Home Runs + 0.288 Batting Average + + What a year! +perl: | + "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" +ruby: | + "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" +python: | + [ + flushLeft( + """ + Sammy Sosa completed another fine season with great stats. + + 63 Home Runs + 0.288 Batting Average + + What a year! + """ + ) + ] +syck: | + struct test_node stream[] = { + { T_STR, 0, "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" }, + end_node + }; + + +--- +test: Indentation determines scope +spec: 2.16 +yaml: | + name: Mark McGwire + accomplishment: > + Mark set a major league + home run record in 1998. + stats: | + 65 Home Runs + 0.278 Batting Average +php: | + array( + 'name' => 'Mark McGwire', + 'accomplishment' => "Mark set a major league home run record in 1998.\n", + 'stats' => "65 Home Runs\n0.278 Batting Average\n" + ) +--- +test: Quoted scalars +todo: true +spec: 2.17 +yaml: | + unicode: "Sosa did fine.\u263A" + control: "\b1998\t1999\t2000\n" + hexesc: "\x0D\x0A is \r\n" + + single: '"Howdy!" he cried.' + quoted: ' # not a ''comment''.' + tie-fighter: '|\-*-/|' +ruby: | + { + "tie-fighter" => "|\\-*-/|", + "control"=>"\0101998\t1999\t2000\n", + "unicode"=>"Sosa did fine." + ["263A".hex ].pack('U*'), + "quoted"=>" # not a 'comment'.", + "single"=>"\"Howdy!\" he cried.", + "hexesc"=>"\r\n is \r\n" + } +--- +test: Multiline flow scalars +todo: true +spec: 2.18 +yaml: | + plain: + This unquoted scalar + spans many lines. + + quoted: "So does this + quoted scalar.\n" +ruby: | + { + 'plain' => 'This unquoted scalar spans many lines.', + 'quoted' => "So does this quoted scalar.\n" + } +--- +test: Integers +spec: 2.19 +yaml: | + canonical: 12345 + decimal: +12,345 + octal: 014 + hexadecimal: 0xC +php: | + array( + 'canonical' => 12345, + 'decimal' => 12345, + 'octal' => 014, + 'hexadecimal' => 0xC + ) +--- +# FIX: spec shows parens around -inf and NaN +test: Floating point +spec: 2.20 +yaml: | + canonical: 1.23015e+3 + exponential: 12.3015e+02 + fixed: 1,230.15 + negative infinity: -.inf + not a number: .NaN +php: | + array( + 'canonical' => 1230.15, + 'exponential' => 1230.15, + 'fixed' => 1230.15, + 'negative infinity' => log(0), + 'not a number' => -log(0), + ) +--- +test: Miscellaneous +spec: 2.21 +yaml: | + null: ~ + true: true + false: false + string: '12345' +php: | + array( + '' => null, + 1 => true, + 0 => false, + 'string' => '12345' + ) +--- +test: Timestamps +todo: true +spec: 2.22 +yaml: | + canonical: 2001-12-15T02:59:43.1Z + iso8601: 2001-12-14t21:59:43.10-05:00 + spaced: 2001-12-14 21:59:43.10 -05:00 + date: 2002-12-14 # Time is noon UTC +php: | + array( + 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), + 'iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), + 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), + 'date' => Date.new( 2002, 12, 14 ) + ) +--- +test: legacy Timestamps test +todo: true +spec: legacy D4 +yaml: | + canonical: 2001-12-15T02:59:43.00Z + iso8601: 2001-02-28t21:59:43.00-05:00 + spaced: 2001-12-14 21:59:43.00 -05:00 + date: 2002-12-14 +php: | + array( + 'canonical' => Time::utc( 2001, 12, 15, 2, 59, 43, 0 ), + 'iso8601' => YAML::mktime( 2001, 2, 28, 21, 59, 43, 0, "-05:00" ), + 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0, "-05:00" ), + 'date' => Date.new( 2002, 12, 14 ) + ) +--- +test: Various explicit families +todo: true +spec: 2.23 +yaml: | + not-date: !str 2002-04-28 + picture: !binary | + R0lGODlhDAAMAIQAAP//9/X + 17unp5WZmZgAAAOfn515eXv + Pz7Y6OjuDg4J+fn5OTk6enp + 56enmleECcgggoBADs= + + application specific tag: !!something | + The semantics of the tag + above may be different for + different documents. + +ruby-setup: | + YAML.add_private_type( "something" ) do |type, val| + "SOMETHING: #{val}" + end +ruby: | + { + 'not-date' => '2002-04-28', + 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", + 'application specific tag' => "SOMETHING: The semantics of the tag\nabove may be different for\ndifferent documents.\n" + } +--- +test: Application specific family +todo: true +spec: 2.24 +yaml: | + # Establish a tag prefix + --- !clarkevans.com,2002/graph/^shape + # Use the prefix: shorthand for + # !clarkevans.com,2002/graph/circle + - !^circle + center: &ORIGIN {x: 73, 'y': 129} + radius: 7 + - !^line # !clarkevans.com,2002/graph/line + start: *ORIGIN + finish: { x: 89, 'y': 102 } + - !^label + start: *ORIGIN + color: 0xFFEEBB + value: Pretty vector drawing. +ruby-setup: | + YAML.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val| + if Array === val + val << "Shape Container" + val + else + raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect + end + } + one_shape_proc = Proc.new { |type, val| + scheme, domain, type = type.split( /:/, 3 ) + if val.is_a? ::Hash + val['TYPE'] = "Shape: #{type}" + val + else + raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect + end + } + YAML.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc ) + YAML.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc ) + YAML.add_domain_type( "clarkevans.com,2002", 'graph/label', &one_shape_proc ) +ruby: | + [ + { + "radius" => 7, + "center"=> + { + "x" => 73, + "y" => 129 + }, + "TYPE" => "Shape: graph/circle" + }, { + "finish" => + { + "x" => 89, + "y" => 102 + }, + "TYPE" => "Shape: graph/line", + "start" => + { + "x" => 73, + "y" => 129 + } + }, { + "TYPE" => "Shape: graph/label", + "value" => "Pretty vector drawing.", + "start" => + { + "x" => 73, + "y" => 129 + }, + "color" => 16772795 + }, + "Shape Container" + ] +# --- +# test: Unordered set +# spec: 2.25 +# yaml: | +# # sets are represented as a +# # mapping where each key is +# # associated with the empty string +# --- !set +# ? Mark McGwire +# ? Sammy Sosa +# ? Ken Griff +--- +test: Ordered mappings +todo: true +spec: 2.26 +yaml: | + # ordered maps are represented as + # a sequence of mappings, with + # each mapping having one key + --- !omap + - Mark McGwire: 65 + - Sammy Sosa: 63 + - Ken Griffy: 58 +ruby: | + YAML::Omap[ + 'Mark McGwire', 65, + 'Sammy Sosa', 63, + 'Ken Griffy', 58 + ] +--- +test: Invoice +dump_skip: true +spec: 2.27 +yaml: | + --- !clarkevans.com,2002/^invoice + invoice: 34843 + date : 2001-01-23 + bill-to: &id001 + given : Chris + family : Dumars + address: + lines: | + 458 Walkman Dr. + Suite #292 + city : Royal Oak + state : MI + postal : 48046 + ship-to: *id001 + product: + - + sku : BL394D + quantity : 4 + description : Basketball + price : 450.00 + - + sku : BL4438H + quantity : 1 + description : Super Hoop + price : 2392.00 + tax : 251.42 + total: 4443.52 + comments: > + Late afternoon is best. + Backup contact is Nancy + Billsmer @ 338-4338. +php: | + array( + 'invoice' => 34843, 'date' => mktime(0, 0, 0, 1, 23, 2001), + 'bill-to' => + array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) + , 'ship-to' => + array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) + , 'product' => + array( + array( 'sku' => 'BL394D', 'quantity' => 4, 'description' => 'Basketball', 'price' => 450.00 ), + array( 'sku' => 'BL4438H', 'quantity' => 1, 'description' => 'Super Hoop', 'price' => 2392.00 ) + ), + 'tax' => 251.42, 'total' => 4443.52, + 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" + ) +--- +test: Log file +todo: true +spec: 2.28 +yaml: | + --- + Time: 2001-11-23 15:01:42 -05:00 + User: ed + Warning: > + This is an error message + for the log file + --- + Time: 2001-11-23 15:02:31 -05:00 + User: ed + Warning: > + A slightly different error + message. + --- + Date: 2001-11-23 15:03:17 -05:00 + User: ed + Fatal: > + Unknown variable "bar" + Stack: + - file: TopClass.py + line: 23 + code: | + x = MoreObject("345\n") + - file: MoreClass.py + line: 58 + code: |- + foo = bar +ruby: | + y = YAML::Stream.new + y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ), + 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } ) + y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ), + 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } ) + y.add( { 'Date' => YAML::mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ), + 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n", + 'Stack' => [ + { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" }, + { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } ) +documents: 3 + +--- +test: Throwaway comments +yaml: | + ### These are four throwaway comment ### + + ### lines (the second line is empty). ### + this: | # Comments may trail lines. + contains three lines of text. + The third one starts with a + # character. This isn't a comment. + + # These are three throwaway comment + # lines (the first line is empty). +php: | + array( + 'this' => "contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n" + ) +--- +test: Document with a single value +todo: true +yaml: | + --- > + This YAML stream contains a single text value. + The next stream is a log file - a sequence of + log entries. Adding an entry to the log is a + simple matter of appending it at the end. +ruby: | + "This YAML stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end.\n" +--- +test: Document stream +todo: true +yaml: | + --- + at: 2001-08-12 09:25:00.00 Z + type: GET + HTTP: '1.0' + url: '/index.html' + --- + at: 2001-08-12 09:25:10.00 Z + type: GET + HTTP: '1.0' + url: '/toc.html' +ruby: | + y = YAML::Stream.new + y.add( { + 'at' => Time::utc( 2001, 8, 12, 9, 25, 00 ), + 'type' => 'GET', + 'HTTP' => '1.0', + 'url' => '/index.html' + } ) + y.add( { + 'at' => Time::utc( 2001, 8, 12, 9, 25, 10 ), + 'type' => 'GET', + 'HTTP' => '1.0', + 'url' => '/toc.html' + } ) +documents: 2 + +--- +test: Top level mapping +yaml: | + # This stream is an example of a top-level mapping. + invoice : 34843 + date : 2001-01-23 + total : 4443.52 +php: | + array( + 'invoice' => 34843, + 'date' => mktime(0, 0, 0, 1, 23, 2001), + 'total' => 4443.52 + ) +--- +test: Single-line documents +todo: true +yaml: | + # The following is a sequence of three documents. + # The first contains an empty mapping, the second + # an empty sequence, and the last an empty string. + --- {} + --- [ ] + --- '' +ruby: | + y = YAML::Stream.new + y.add( {} ) + y.add( [] ) + y.add( '' ) +documents: 3 + +--- +test: Document with pause +todo: true +yaml: | + # A communication channel based on a YAML stream. + --- + sent at: 2002-06-06 11:46:25.10 Z + payload: Whatever + # Receiver can process this as soon as the following is sent: + ... + # Even if the next message is sent long after: + --- + sent at: 2002-06-06 12:05:53.47 Z + payload: Whatever + ... +ruby: | + y = YAML::Stream.new + y.add( + { 'sent at' => YAML::mktime( 2002, 6, 6, 11, 46, 25, 0.10 ), + 'payload' => 'Whatever' } + ) + y.add( + { "payload" => "Whatever", "sent at" => YAML::mktime( 2002, 6, 6, 12, 5, 53, 0.47 ) } + ) +documents: 2 + +--- +test: Explicit typing +yaml: | + integer: 12 + also int: ! "12" + string: !str 12 +php: | + array( 'integer' => 12, 'also int' => 12, 'string' => '12' ) +--- +test: Private types +todo: true +yaml: | + # Both examples below make use of the 'x-private:ball' + # type family URI, but with different semantics. + --- + pool: !!ball + number: 8 + color: black + --- + bearing: !!ball + material: steel +ruby: | + y = YAML::Stream.new + y.add( { 'pool' => + YAML::PrivateType.new( 'ball', + { 'number' => 8, 'color' => 'black' } ) } + ) + y.add( { 'bearing' => + YAML::PrivateType.new( 'ball', + { 'material' => 'steel' } ) } + ) +documents: 2 + +--- +test: Type family under yaml.org +yaml: | + # The URI is 'tag:yaml.org,2002:str' + - !str a Unicode string +php: | + array( 'a Unicode string' ) +--- +test: Type family under perl.yaml.org +todo: true +yaml: | + # The URI is 'tag:perl.yaml.org,2002:Text::Tabs' + - !perl/Text::Tabs {} +ruby: | + [ YAML::DomainType.new( 'perl.yaml.org,2002', 'Text::Tabs', {} ) ] +--- +test: Type family under clarkevans.com +todo: true +yaml: | + # The URI is 'tag:clarkevans.com,2003-02:timesheet' + - !clarkevans.com,2003-02/timesheet {} +ruby: | + [ YAML::DomainType.new( 'clarkevans.com,2003-02', 'timesheet', {} ) ] +--- +test: URI Escaping +todo: true +yaml: | + same: + - !domain.tld,2002/type\x30 value + - !domain.tld,2002/type0 value + different: # As far as the YAML parser is concerned + - !domain.tld,2002/type%30 value + - !domain.tld,2002/type0 value +ruby-setup: | + YAML.add_domain_type( "domain.tld,2002", "type0" ) { |type, val| + "ONE: #{val}" + } + YAML.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val| + "TWO: #{val}" + } +ruby: | + { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value', 'ONE: value' ] } +--- +test: URI Prefixing +todo: true +yaml: | + # 'tag:domain.tld,2002:invoice' is some type family. + invoice: !domain.tld,2002/^invoice + # 'seq' is shorthand for 'tag:yaml.org,2002:seq'. + # This does not effect '^customer' below + # because it is does not specify a prefix. + customers: !seq + # '^customer' is shorthand for the full + # notation 'tag:domain.tld,2002:customer'. + - !^customer + given : Chris + family : Dumars +ruby-setup: | + YAML.add_domain_type( "domain.tld,2002", /(invoice|customer)/ ) { |type, val| + if val.is_a? ::Hash + scheme, domain, type = type.split( /:/, 3 ) + val['type'] = "domain #{type}" + val + else + raise YAML::Error, "Not a Hash in domain.tld/invoice: " + val.inspect + end + } +ruby: | + { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } } + +--- +test: Overriding anchors +yaml: | + anchor : &A001 This scalar has an anchor. + override : &A001 > + The alias node below is a + repeated use of this value. + alias : *A001 +php: | + array( 'anchor' => 'This scalar has an anchor.', + 'override' => "The alias node below is a repeated use of this value.\n", + 'alias' => "The alias node below is a repeated use of this value.\n" ) +--- +test: Flow and block formatting +todo: true +yaml: | + empty: [] + flow: [ one, two, three # May span lines, + , four, # indentation is + five ] # mostly ignored. + block: + - First item in top sequence + - + - Subordinate sequence entry + - > + A folded sequence entry + - Sixth item in top sequence +ruby: | + { 'empty' => [], 'flow' => [ 'one', 'two', 'three', 'four', 'five' ], + 'block' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ], + "A folded sequence entry\n", 'Sixth item in top sequence' ] } +--- +test: Complete mapping test +todo: true +yaml: | + empty: {} + flow: { one: 1, two: 2 } + spanning: { one: 1, + two: 2 } + block: + first : First entry + second: + key: Subordinate mapping + third: + - Subordinate sequence + - { } + - Previous mapping is empty. + - A key: value pair in a sequence. + A second: key:value pair. + - The previous entry is equal to the following one. + - + A key: value pair in a sequence. + A second: key:value pair. + !float 12 : This key is a float. + ? > + ? + : This key had to be protected. + "\a" : This key had to be escaped. + ? > + This is a + multi-line + folded key + : Whose value is + also multi-line. + ? this also works as a key + : with a value at the next line. + ? + - This key + - is a sequence + : + - With a sequence value. + ? + This: key + is a: mapping + : + with a: mapping value. +ruby: | + { 'empty' => {}, 'flow' => { 'one' => 1, 'two' => 2 }, + 'spanning' => { 'one' => 1, 'two' => 2 }, + 'block' => { 'first' => 'First entry', 'second' => + { 'key' => 'Subordinate mapping' }, 'third' => + [ 'Subordinate sequence', {}, 'Previous mapping is empty.', + { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' }, + 'The previous entry is equal to the following one.', + { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ], + 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.', + "\a" => 'This key had to be escaped.', + "This is a multi-line folded key\n" => "Whose value is also multi-line.", + 'this also works as a key' => 'with a value at the next line.', + [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } } + # Couldn't recreate map exactly, so we'll do a detailed check to be sure it's entact + obj_y['block'].keys.each { |k| + if Hash === k + v = obj_y['block'][k] + if k['This'] == 'key' and k['is a'] == 'mapping' and v['with a'] == 'mapping value.' + obj_r['block'][k] = v + end + end + } +--- +test: Literal explicit indentation +yaml: | + # Explicit indentation must + # be given in all the three + # following cases. + leading spaces: |2 + This value starts with four spaces. + + leading line break: |2 + + This value starts with a line break. + + leading comment indicator: |2 + # first line starts with a + # character. + + # Explicit indentation may + # also be given when it is + # not required. + redundant: |2 + This value is indented 2 spaces. +php: | + array( + 'leading spaces' => " This value starts with four spaces.\n", + 'leading line break' => "\nThis value starts with a line break.\n", + 'leading comment indicator' => "# first line starts with a\n# character.\n", + 'redundant' => "This value is indented 2 spaces.\n" + ) +--- +test: Chomping and keep modifiers +yaml: | + clipped: | + This has one newline. + + same as "clipped" above: "This has one newline.\n" + + stripped: |- + This has no newline. + + same as "stripped" above: "This has no newline." + + kept: |+ + This has two newlines. + + same as "kept" above: "This has two newlines.\n\n" +php: | + array( + 'clipped' => "This has one newline.\n", + 'same as "clipped" above' => "This has one newline.\n", + 'stripped' => 'This has no newline.', + 'same as "stripped" above' => 'This has no newline.', + 'kept' => "This has two newlines.\n\n", + 'same as "kept" above' => "This has two newlines.\n\n" + ) +--- +test: Literal combinations +todo: true +yaml: | + empty: | + + literal: | + The \ ' " characters may be + freely used. Leading white + space is significant. + + Line breaks are significant. + Thus this value contains one + empty line and ends with a + single line break, but does + not start with one. + + is equal to: "The \\ ' \" characters may \ + be\nfreely used. Leading white\n space \ + is significant.\n\nLine breaks are \ + significant.\nThus this value contains \ + one\nempty line and ends with a\nsingle \ + line break, but does\nnot start with one.\n" + + # Comments may follow a block + # scalar value. They must be + # less indented. + + # Modifiers may be combined in any order. + indented and chomped: |2- + This has no newline. + + also written as: |-2 + This has no newline. + + both are equal to: " This has no newline." +php: | + array( + 'empty' => '', + 'literal' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + + "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + + "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", + 'is equal to' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + + "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + + "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", + 'indented and chomped' => ' This has no newline.', + 'also written as' => ' This has no newline.', + 'both are equal to' => ' This has no newline.' + ) +--- +test: Folded combinations +todo: true +yaml: | + empty: > + + one paragraph: > + Line feeds are converted + to spaces, so this value + contains no line breaks + except for the final one. + + multiple paragraphs: >2 + + An empty line, either + at the start or in + the value: + + Is interpreted as a + line break. Thus this + value contains three + line breaks. + + indented text: > + This is a folded + paragraph followed + by a list: + * first entry + * second entry + Followed by another + folded paragraph, + another list: + + * first entry + + * second entry + + And a final folded + paragraph. + + above is equal to: | + This is a folded paragraph followed by a list: + * first entry + * second entry + Followed by another folded paragraph, another list: + + * first entry + + * second entry + + And a final folded paragraph. + + # Explicit comments may follow + # but must be less indented. +php: | + array( + 'empty' => '', + 'one paragraph' => 'Line feeds are converted to spaces, so this value'. + " contains no line breaks except for the final one.\n", + 'multiple paragraphs' => "\nAn empty line, either at the start or in the value:\n". + "Is interpreted as a line break. Thus this value contains three line breaks.\n", + 'indented text' => "This is a folded paragraph followed by a list:\n". + " * first entry\n * second entry\nFollowed by another folded paragraph, ". + "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n", + 'above is equal to' => "This is a folded paragraph followed by a list:\n". + " * first entry\n * second entry\nFollowed by another folded paragraph, ". + "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n" + ) +--- +test: Single quotes +todo: true +yaml: | + empty: '' + second: '! : \ etc. can be used freely.' + third: 'a single quote '' must be escaped.' + span: 'this contains + six spaces + + and one + line break' + is same as: "this contains six spaces\nand one line break" +php: | + array( + 'empty' => '', + 'second' => '! : \\ etc. can be used freely.', + 'third' => "a single quote ' must be escaped.", + 'span' => "this contains six spaces\nand one line break", + 'is same as' => "this contains six spaces\nand one line break" + ) +--- +test: Double quotes +todo: true +yaml: | + empty: "" + second: "! : etc. can be used freely." + third: "a \" or a \\ must be escaped." + fourth: "this value ends with an LF.\n" + span: "this contains + four \ + spaces" + is equal to: "this contains four spaces" +php: | + array( + 'empty' => '', + 'second' => '! : etc. can be used freely.', + 'third' => 'a " or a \\ must be escaped.', + 'fourth' => "this value ends with an LF.\n", + 'span' => "this contains four spaces", + 'is equal to' => "this contains four spaces" + ) +--- +test: Unquoted strings +todo: true +yaml: | + first: There is no unquoted empty string. + + second: 12 ## This is an integer. + + third: !str 12 ## This is a string. + + span: this contains + six spaces + + and one + line break + + indicators: this has no comments. + #:foo and bar# are + both text. + + flow: [ can span + lines, # comment + like + this ] + + note: { one-line keys: but multi-line values } + +php: | + array( + 'first' => 'There is no unquoted empty string.', + 'second' => 12, + 'third' => '12', + 'span' => "this contains six spaces\nand one line break", + 'indicators' => "this has no comments. #:foo and bar# are both text.", + 'flow' => [ 'can span lines', 'like this' ], + 'note' => { 'one-line keys' => 'but multi-line values' } + ) +--- +test: Spanning sequences +todo: true +yaml: | + # The following are equal seqs + # with different identities. + flow: [ one, two ] + spanning: [ one, + two ] + block: + - one + - two +php: | + array( + 'flow' => [ 'one', 'two' ], + 'spanning' => [ 'one', 'two' ], + 'block' => [ 'one', 'two' ] + ) +--- +test: Flow mappings +yaml: | + # The following are equal maps + # with different identities. + flow: { one: 1, two: 2 } + block: + one: 1 + two: 2 +php: | + array( + 'flow' => array( 'one' => 1, 'two' => 2 ), + 'block' => array( 'one' => 1, 'two' => 2 ) + ) +--- +test: Representations of 12 +todo: true +yaml: | + - 12 # An integer + # The following scalars + # are loaded to the + # string value '1' '2'. + - !str 12 + - '12' + - "12" + - "\ + 1\ + 2\ + " + # Strings containing paths and regexps can be unquoted: + - /foo/bar + - d:/foo/bar + - foo/bar + - /a.*b/ +php: | + array( 12, '12', '12', '12', '12', '/foo/bar', 'd:/foo/bar', 'foo/bar', '/a.*b/' ) +--- +test: "Null" +todo: true +yaml: | + canonical: ~ + + english: null + + # This sequence has five + # entries, two with values. + sparse: + - ~ + - 2nd entry + - Null + - 4th entry + - + + four: This mapping has five keys, + only two with values. + +php: | + array ( + 'canonical' => null, + 'english' => null, + 'sparse' => array( null, '2nd entry', null, '4th entry', null ]), + 'four' => 'This mapping has five keys, only two with values.' + ) +--- +test: Omap +todo: true +yaml: | + # Explicitly typed dictionary. + Bestiary: !omap + - aardvark: African pig-like ant eater. Ugly. + - anteater: South-American ant eater. Two species. + - anaconda: South-American constrictor snake. Scary. + # Etc. +ruby: | + { + 'Bestiary' => YAML::Omap[ + 'aardvark', 'African pig-like ant eater. Ugly.', + 'anteater', 'South-American ant eater. Two species.', + 'anaconda', 'South-American constrictor snake. Scary.' + ] + } + +--- +test: Pairs +todo: true +yaml: | + # Explicitly typed pairs. + tasks: !pairs + - meeting: with team. + - meeting: with boss. + - break: lunch. + - meeting: with client. +ruby: | + { + 'tasks' => YAML::Pairs[ + 'meeting', 'with team.', + 'meeting', 'with boss.', + 'break', 'lunch.', + 'meeting', 'with client.' + ] + } + +--- +test: Set +todo: true +yaml: | + # Explicitly typed set. + baseball players: !set + Mark McGwire: + Sammy Sosa: + Ken Griffey: +ruby: | + { + 'baseball players' => YAML::Set[ + 'Mark McGwire', nil, + 'Sammy Sosa', nil, + 'Ken Griffey', nil + ] + } + +--- +test: Boolean +yaml: | + false: used as key + logical: true + answer: false +php: | + array( + false => 'used as key', + 'logical' => true, + 'answer' => false + ) +--- +test: Integer +yaml: | + canonical: 12345 + decimal: +12,345 + octal: 014 + hexadecimal: 0xC +php: | + array( + 'canonical' => 12345, + 'decimal' => 12345, + 'octal' => 12, + 'hexadecimal' => 12 + ) +--- +test: Float +yaml: | + canonical: 1.23015e+3 + exponential: 12.3015e+02 + fixed: 1,230.15 + negative infinity: -.inf + not a number: .NaN +php: | + array( + 'canonical' => 1230.15, + 'exponential' => 1230.15, + 'fixed' => 1230.15, + 'negative infinity' => log(0), + 'not a number' => -log(0) + ) +--- +test: Timestamp +todo: true +yaml: | + canonical: 2001-12-15T02:59:43.1Z + valid iso8601: 2001-12-14t21:59:43.10-05:00 + space separated: 2001-12-14 21:59:43.10 -05:00 + date (noon UTC): 2002-12-14 +ruby: | + array( + 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), + 'valid iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), + 'space separated' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), + 'date (noon UTC)' => Date.new( 2002, 12, 14 ) + ) +--- +test: Binary +todo: true +yaml: | + canonical: !binary "\ + R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\ + OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\ + +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\ + AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=" + base64: !binary | + R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 + OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ + +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC + AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= + description: > + The binary value above is a tiny arrow + encoded as a gif image. +ruby-setup: | + arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;" +ruby: | + { + 'canonical' => arrow_gif, + 'base64' => arrow_gif, + 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" + } + +--- +test: Merge key +todo: true +yaml: | + --- + - &CENTER { x: 1, y: 2 } + - &LEFT { x: 0, y: 2 } + - &BIG { r: 10 } + - &SMALL { r: 1 } + + # All the following maps are equal: + + - # Explicit keys + x: 1 + y: 2 + r: 10 + label: center/big + + - # Merge one map + << : *CENTER + r: 10 + label: center/big + + - # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + + - # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big + +ruby-setup: | + center = { 'x' => 1, 'y' => 2 } + left = { 'x' => 0, 'y' => 2 } + big = { 'r' => 10 } + small = { 'r' => 1 } + node1 = { 'x' => 1, 'y' => 2, 'r' => 10, 'label' => 'center/big' } + node2 = center.dup + node2.update( { 'r' => 10, 'label' => 'center/big' } ) + node3 = big.dup + node3.update( center ) + node3.update( { 'label' => 'center/big' } ) + node4 = small.dup + node4.update( left ) + node4.update( big ) + node4.update( { 'x' => 1, 'label' => 'center/big' } ) + +ruby: | + [ + center, left, big, small, node1, node2, node3, node4 + ] + +--- +test: Default key +todo: true +yaml: | + --- # Old schema + link with: + - library1.dll + - library2.dll + --- # New schema + link with: + - = : library1.dll + version: 1.2 + - = : library2.dll + version: 2.3 +ruby: | + y = YAML::Stream.new + y.add( { 'link with' => [ 'library1.dll', 'library2.dll' ] } ) + obj_h = Hash[ 'version' => 1.2 ] + obj_h.default = 'library1.dll' + obj_h2 = Hash[ 'version' => 2.3 ] + obj_h2.default = 'library2.dll' + y.add( { 'link with' => [ obj_h, obj_h2 ] } ) +documents: 2 + +--- +test: Special keys +todo: true +yaml: | + "!": These three keys + "&": had to be quoted + "=": and are normal strings. + # NOTE: the following node should NOT be serialized this way. + encoded node : + !special '!' : '!type' + !special|canonical '&' : 12 + = : value + # The proper way to serialize the above node is as follows: + node : !!type &12 value +ruby: | + { '!' => 'These three keys', '&' => 'had to be quoted', + '=' => 'and are normal strings.', + 'encoded node' => YAML::PrivateType.new( 'type', 'value' ), + 'node' => YAML::PrivateType.new( 'type', 'value' ) } diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml new file mode 100644 index 000000000..aac4e6807 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml @@ -0,0 +1,244 @@ +--- %YAML:1.0 +test: Strings +brief: > + Any group of characters beginning with an + alphabetic or numeric character is a string, + unless it belongs to one of the groups below + (such as an Integer or Time). +yaml: | + String +php: | + 'String' +--- +test: String characters +brief: > + A string can contain any alphabetic or + numeric character, along with many + punctuation characters, including the + period, dash, space, quotes, exclamation, and + question mark. +yaml: | + - What's Yaml? + - It's for writing data structures in plain text. + - And? + - And what? That's not good enough for you? + - No, I mean, "And what about Yaml?" + - Oh, oh yeah. Uh.. Yaml for Ruby. +php: | + array( + "What's Yaml?", + "It's for writing data structures in plain text.", + "And?", + "And what? That's not good enough for you?", + "No, I mean, \"And what about Yaml?\"", + "Oh, oh yeah. Uh.. Yaml for Ruby." + ) +--- +test: Indicators in Strings +brief: > + Be careful using indicators in strings. In particular, + the comma, colon, and pound sign must be used carefully. +yaml: | + the colon followed by space is an indicator: but is a string:right here + same for the pound sign: here we have it#in a string + the comma can, honestly, be used in most cases: [ but not in, inline collections ] +php: | + array( + 'the colon followed by space is an indicator' => 'but is a string:right here', + 'same for the pound sign' => 'here we have it#in a string', + 'the comma can, honestly, be used in most cases' => array('but not in', 'inline collections') + ) +--- +test: Forcing Strings +brief: > + Any YAML type can be forced into a string using the + explicit !str method. +yaml: | + date string: !str 2001-08-01 + number string: !str 192 +php: | + array( + 'date string' => '2001-08-01', + 'number string' => '192' + ) +--- +test: Single-quoted Strings +brief: > + You can also enclose your strings within single quotes, + which allows use of slashes, colons, and other indicators + freely. Inside single quotes, you can represent a single + quote in your string by using two single quotes next to + each other. +yaml: | + all my favorite symbols: '#:!/%.)' + a few i hate: '&(*' + why do i hate them?: 'it''s very hard to explain' + entities: '£ me' +php: | + array( + 'all my favorite symbols' => '#:!/%.)', + 'a few i hate' => '&(*', + 'why do i hate them?' => 'it\'s very hard to explain', + 'entities' => '£ me' + ) +--- +test: Double-quoted Strings +brief: > + Enclosing strings in double quotes allows you + to use escapings to represent ASCII and + Unicode characters. +yaml: | + i know where i want my line breaks: "one here\nand another here\n" +php: | + array( + 'i know where i want my line breaks' => "one here\nand another here\n" + ) +--- +test: Multi-line Quoted Strings +todo: true +brief: > + Both single- and double-quoted strings may be + carried on to new lines in your YAML document. + They must be indented a step and indentation + is interpreted as a single space. +yaml: | + i want a long string: "so i'm going to + let it go on and on to other lines + until i end it with a quote." +php: | + array('i want a long string' => "so i'm going to ". + "let it go on and on to other lines ". + "until i end it with a quote." + ) + +--- +test: Plain scalars +todo: true +brief: > + Unquoted strings may also span multiple lines, if they + are free of YAML space indicators and indented. +yaml: | + - My little toe is broken in two places; + - I'm crazy to have skied this way; + - I'm not the craziest he's seen, since there was always the German guy + who skied for 3 hours on a broken shin bone (just below the kneecap); + - Nevertheless, second place is respectable, and he doesn't + recommend going for the record; + - He's going to put my foot in plaster for a month; + - This would impair my skiing ability somewhat for the + duration, as can be imagined. +php: | + array( + "My little toe is broken in two places;", + "I'm crazy to have skied this way;", + "I'm not the craziest he's seen, since there was always ". + "the German guy who skied for 3 hours on a broken shin ". + "bone (just below the kneecap);", + "Nevertheless, second place is respectable, and he doesn't ". + "recommend going for the record;", + "He's going to put my foot in plaster for a month;", + "This would impair my skiing ability somewhat for the duration, ". + "as can be imagined." + ) +--- +test: 'Null' +brief: > + You can use the tilde '~' character for a null value. +yaml: | + name: Mr. Show + hosted by: Bob and David + date of next season: ~ +php: | + array( + 'name' => 'Mr. Show', + 'hosted by' => 'Bob and David', + 'date of next season' => null + ) +--- +test: Boolean +brief: > + You can use 'true' and 'false' for Boolean values. +yaml: | + Is Gus a Liar?: true + Do I rely on Gus for Sustenance?: false +php: | + array( + 'Is Gus a Liar?' => true, + 'Do I rely on Gus for Sustenance?' => false + ) +--- +test: Integers +dump_skip: true +brief: > + An integer is a series of numbers, optionally + starting with a positive or negative sign. Integers + may also contain commas for readability. +yaml: | + zero: 0 + simple: 12 + one-thousand: 1,000 + negative one-thousand: -1,000 +php: | + array( + 'zero' => 0, + 'simple' => 12, + 'one-thousand' => 1000, + 'negative one-thousand' => -1000 + ) +--- +test: Integers as Map Keys +brief: > + An integer can be used a dictionary key. +yaml: | + 1: one + 2: two + 3: three +php: | + array( + 1 => 'one', + 2 => 'two', + 3 => 'three' + ) +--- +test: Floats +dump_skip: true +brief: > + Floats are represented by numbers with decimals, + allowing for scientific notation, as well as + positive and negative infinity and "not a number." +yaml: | + a simple float: 2.00 + larger float: 1,000.09 + scientific notation: 1.00009e+3 +php: | + array( + 'a simple float' => 2.0, + 'larger float' => 1000.09, + 'scientific notation' => 1000.09 + ) +--- +test: Time +todo: true +brief: > + You can represent timestamps by using + ISO8601 format, or a variation which + allows spaces between the date, time and + time zone. +yaml: | + iso8601: 2001-12-14t21:59:43.10-05:00 + space separated: 2001-12-14 21:59:43.10 -05:00 +php: | + array( + 'iso8601' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), + 'space separated' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ) + ) +--- +test: Date +todo: true +brief: > + A date can be represented by its year, + month and day in ISO8601 order. +yaml: | + 1976-07-31 +php: | + date( 1976, 7, 31 ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml new file mode 100644 index 000000000..ec456ed09 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml @@ -0,0 +1 @@ +value: diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml new file mode 100644 index 000000000..09bf86e79 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml @@ -0,0 +1,147 @@ +test: outside double quotes +yaml: | + \0 \ \a \b \n +php: | + "\\0 \\ \\a \\b \\n" +--- +test: null +yaml: | + "\0" +php: | + "\x00" +--- +test: bell +yaml: | + "\a" +php: | + "\x07" +--- +test: backspace +yaml: | + "\b" +php: | + "\x08" +--- +test: horizontal tab (1) +yaml: | + "\t" +php: | + "\x09" +--- +test: horizontal tab (2) +yaml: | + "\ " +php: | + "\x09" +--- +test: line feed +yaml: | + "\n" +php: | + "\x0a" +--- +test: vertical tab +yaml: | + "\v" +php: | + "\x0b" +--- +test: form feed +yaml: | + "\f" +php: | + "\x0c" +--- +test: carriage return +yaml: | + "\r" +php: | + "\x0d" +--- +test: escape +yaml: | + "\e" +php: | + "\x1b" +--- +test: space +yaml: | + "\ " +php: | + "\x20" +--- +test: slash +yaml: | + "\/" +php: | + "\x2f" +--- +test: backslash +yaml: | + "\\" +php: | + "\\" +--- +test: Unicode next line +yaml: | + "\N" +php: | + "\xc2\x85" +--- +test: Unicode non-breaking space +yaml: | + "\_" +php: | + "\xc2\xa0" +--- +test: Unicode line separator +yaml: | + "\L" +php: | + "\xe2\x80\xa8" +--- +test: Unicode paragraph separator +yaml: | + "\P" +php: | + "\xe2\x80\xa9" +--- +test: Escaped 8-bit Unicode +yaml: | + "\x42" +php: | + "B" +--- +test: Escaped 16-bit Unicode +yaml: | + "\u20ac" +php: | + "\xe2\x82\xac" +--- +test: Escaped 32-bit Unicode +yaml: | + "\U00000043" +php: | + "C" +--- +test: Example 5.13 Escaped Characters +note: | + Currently throws an error parsing first line. Maybe Symfony Yaml doesn't support + continuation of string across multiple lines? Keeping test here but disabled. +todo: true +yaml: | + "Fun with \\ + \" \a \b \e \f \ + \n \r \t \v \0 \ + \ \_ \N \L \P \ + \x41 \u0041 \U00000041" +php: | + "Fun with \x5C\n\x22 \x07 \x08 \x1B \x0C\n\x0A \x0D \x09 \x0B \x00\n\x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9\nA A A" +--- +test: Double quotes with a line feed +yaml: | + { double: "some value\n \"some quoted string\" and 'some single quotes one'" } +php: | + array( + 'double' => "some value\n \"some quoted string\" and 'some single quotes one'" + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml new file mode 100644 index 000000000..3216a89eb --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml @@ -0,0 +1,18 @@ +- escapedCharacters +- sfComments +- sfCompact +- sfTests +- sfObjects +- sfMergeKey +- sfQuotes +- YtsAnchorAlias +- YtsBasicTests +- YtsBlockMapping +- YtsDocumentSeparator +- YtsErrorTests +- YtsFlowCollections +- YtsFoldedScalars +- YtsNullsAndEmpties +- YtsSpecificationExamples +- YtsTypeTransfers +- unindentedCollections diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml new file mode 100644 index 000000000..46addfcd3 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml @@ -0,0 +1,65 @@ +--- %YAML:1.0 +test: Comments at the end of a line +brief: > + Comments at the end of a line +yaml: | + ex1: "foo # bar" + ex2: "foo # bar" # comment + ex3: 'foo # bar' # comment + ex4: foo # comment +php: | + array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') +--- +test: Comments in the middle +brief: > + Comments in the middle +yaml: | + foo: + # some comment + # some comment + bar: foo + # some comment + # some comment +php: | + array('foo' => array('bar' => 'foo')) +--- +test: Comments on a hash line +brief: > + Comments on a hash line +yaml: | + foo: # a comment + foo: bar # a comment +php: | + array('foo' => array('foo' => 'bar')) +--- +test: 'Value starting with a #' +brief: > + 'Value starting with a #' +yaml: | + foo: '#bar' +php: | + array('foo' => '#bar') +--- +test: Document starting with a comment and a separator +brief: > + Commenting before document start is allowed +yaml: | + # document comment + --- + foo: bar # a comment +php: | + array('foo' => 'bar') +--- +test: Comment containing a colon on a hash line +brief: > + Comment containing a colon on a scalar line +yaml: 'foo # comment: this is also part of the comment' +php: | + 'foo' +--- +test: 'Hash key containing a #' +brief: > + 'Hash key containing a #' +yaml: 'foo#bar: baz' +php: | + array('foo#bar' => 'baz') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml new file mode 100644 index 000000000..1339d23a6 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml @@ -0,0 +1,159 @@ +--- %YAML:1.0 +test: Compact notation +brief: | + Compact notation for sets of mappings with single element +yaml: | + --- + # products purchased + - item : Super Hoop + - item : Basketball + quantity: 1 + - item: + name: Big Shoes + nick: Biggies + quantity: 1 +php: | + array ( + array ( + 'item' => 'Super Hoop', + ), + array ( + 'item' => 'Basketball', + 'quantity' => 1, + ), + array ( + 'item' => array( + 'name' => 'Big Shoes', + 'nick' => 'Biggies' + ), + 'quantity' => 1 + ) + ) +--- +test: Compact notation combined with inline notation +brief: | + Combinations of compact and inline notation are allowed +yaml: | + --- + items: + - { item: Super Hoop, quantity: 1 } + - [ Basketball, Big Shoes ] +php: | + array ( + 'items' => array ( + array ( + 'item' => 'Super Hoop', + 'quantity' => 1, + ), + array ( + 'Basketball', + 'Big Shoes' + ) + ) + ) +--- %YAML:1.0 +test: Compact notation +brief: | + Compact notation for sets of mappings with single element +yaml: | + --- + # products purchased + - item : Super Hoop + - item : Basketball + quantity: 1 + - item: + name: Big Shoes + nick: Biggies + quantity: 1 +php: | + array ( + array ( + 'item' => 'Super Hoop', + ), + array ( + 'item' => 'Basketball', + 'quantity' => 1, + ), + array ( + 'item' => array( + 'name' => 'Big Shoes', + 'nick' => 'Biggies' + ), + 'quantity' => 1 + ) + ) +--- +test: Compact notation combined with inline notation +brief: | + Combinations of compact and inline notation are allowed +yaml: | + --- + items: + - { item: Super Hoop, quantity: 1 } + - [ Basketball, Big Shoes ] +php: | + array ( + 'items' => array ( + array ( + 'item' => 'Super Hoop', + 'quantity' => 1, + ), + array ( + 'Basketball', + 'Big Shoes' + ) + ) + ) +--- %YAML:1.0 +test: Compact notation +brief: | + Compact notation for sets of mappings with single element +yaml: | + --- + # products purchased + - item : Super Hoop + - item : Basketball + quantity: 1 + - item: + name: Big Shoes + nick: Biggies + quantity: 1 +php: | + array ( + array ( + 'item' => 'Super Hoop', + ), + array ( + 'item' => 'Basketball', + 'quantity' => 1, + ), + array ( + 'item' => array( + 'name' => 'Big Shoes', + 'nick' => 'Biggies' + ), + 'quantity' => 1 + ) + ) +--- +test: Compact notation combined with inline notation +brief: | + Combinations of compact and inline notation are allowed +yaml: | + --- + items: + - { item: Super Hoop, quantity: 1 } + - [ Basketball, Big Shoes ] +php: | + array ( + 'items' => array ( + array ( + 'item' => 'Super Hoop', + 'quantity' => 1, + ), + array ( + 'Basketball', + 'Big Shoes' + ) + ) + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml new file mode 100644 index 000000000..3eec4f877 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml @@ -0,0 +1,27 @@ +--- %YAML:1.0 +test: Simple In Place Substitution +brief: > + If you want to reuse an entire alias, only overwriting what is different + you can use a << in place substitution. This is not part of the official + YAML spec, but a widely implemented extension. See the following URL for + details: http://yaml.org/type/merge.html +yaml: | + foo: &foo + a: Steve + b: Clark + c: Brian + bar: &bar + <<: *foo + x: Oren + foo2: &foo2 + a: Ballmer + ding: &dong [ fi, fei, fo, fam] + check: + <<: + - *foo + - *dong + isit: tested + head: + <<: [ *foo , *dong , *foo2 ] +php: | + array('foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), 'bar' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'x' => 'Oren'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Ballmer', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml new file mode 100644 index 000000000..ee124b244 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml @@ -0,0 +1,11 @@ +--- %YAML:1.0 +test: Objects +brief: > + Comments at the end of a line +yaml: | + ex1: "foo # bar" + ex2: "foo # bar" # comment + ex3: 'foo # bar' # comment + ex4: foo # comment +php: | + array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml new file mode 100644 index 000000000..741f1befe --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml @@ -0,0 +1,33 @@ +--- %YAML:1.0 +test: Some characters at the beginning of a string must be escaped +brief: > + Some characters at the beginning of a string must be escaped +yaml: | + foo: | bar +php: | + array('foo' => '| bar') +--- +test: A key can be a quoted string +brief: > + A key can be a quoted string +yaml: | + "foo1": bar + 'foo2': bar + "foo \" bar": bar + 'foo '' bar': bar + 'foo3: ': bar + "foo4: ": bar + foo5: { "foo \" bar: ": bar, 'foo '' bar: ': bar } +php: | + array( + 'foo1' => 'bar', + 'foo2' => 'bar', + 'foo " bar' => 'bar', + 'foo \' bar' => 'bar', + 'foo3: ' => 'bar', + 'foo4: ' => 'bar', + 'foo5' => array( + 'foo " bar: ' => 'bar', + 'foo \' bar: ' => 'bar', + ), + ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml new file mode 100644 index 000000000..7a54f1639 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml @@ -0,0 +1,135 @@ +--- %YAML:1.0 +test: Multiple quoted string on one line +brief: > + Multiple quoted string on one line +yaml: | + stripped_title: { name: "foo bar", help: "bar foo" } +php: | + array('stripped_title' => array('name' => 'foo bar', 'help' => 'bar foo')) +--- +test: Empty sequence +yaml: | + foo: [ ] +php: | + array('foo' => array()) +--- +test: Empty value +yaml: | + foo: +php: | + array('foo' => null) +--- +test: Inline string parsing +brief: > + Inline string parsing +yaml: | + test: ['complex: string', 'another [string]'] +php: | + array('test' => array('complex: string', 'another [string]')) +--- +test: Boolean +brief: > + Boolean +yaml: | + - false + - true + - null + - ~ + - 'false' + - 'true' + - 'null' + - '~' +php: | + array( + false, + true, + null, + null, + 'false', + 'true', + 'null', + '~', + ) +--- +test: Empty lines in folded blocks +brief: > + Empty lines in folded blocks +yaml: | + foo: + bar: | + foo + + + + bar +php: | + array('foo' => array('bar' => "foo\n\n\n \nbar\n")) +--- +test: IP addresses +brief: > + IP addresses +yaml: | + foo: 10.0.0.2 +php: | + array('foo' => '10.0.0.2') +--- +test: A sequence with an embedded mapping +brief: > + A sequence with an embedded mapping +yaml: | + - foo + - bar: { bar: foo } +php: | + array('foo', array('bar' => array('bar' => 'foo'))) +--- +test: A sequence with an unordered array +brief: > + A sequence with an unordered array +yaml: | + 1: foo + 0: bar +php: | + array(1 => 'foo', 0 => 'bar') +--- +test: Octal +brief: as in spec example 2.19, octal value is converted +yaml: | + foo: 0123 +php: | + array('foo' => 83) +--- +test: Octal strings +brief: Octal notation in a string must remain a string +yaml: | + foo: "0123" +php: | + array('foo' => '0123') +--- +test: Octal strings +brief: Octal notation in a string must remain a string +yaml: | + foo: '0123' +php: | + array('foo' => '0123') +--- +test: Octal strings +brief: Octal notation in a string must remain a string +yaml: | + foo: | + 0123 +php: | + array('foo' => "0123\n") +--- +test: Document as a simple hash +brief: Document as a simple hash +yaml: | + { foo: bar } +php: | + array('foo' => 'bar') +--- +test: Document as a simple array +brief: Document as a simple array +yaml: | + [ foo, bar ] +php: | + array('foo', 'bar') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml new file mode 100644 index 000000000..fd8ad7ed4 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml @@ -0,0 +1,62 @@ +--- %YAML:1.0 +test: Unindented collection +brief: > + Unindented collection +yaml: | + collection: + - item1 + - item2 + - item3 +php: | + array('collection' => array('item1', 'item2', 'item3')) +--- +test: Nested unindented collection (two levels) +brief: > + Nested unindented collection +yaml: | + collection: + key: + - a + - b + - c +php: | + array('collection' => array('key' => array('a', 'b', 'c'))) +--- +test: Nested unindented collection (three levels) +brief: > + Nested unindented collection +yaml: | + collection: + key: + subkey: + - one + - two + - three +php: | + array('collection' => array('key' => array('subkey' => array('one', 'two', 'three')))) +--- +test: Key/value after unindented collection (1) +brief: > + Key/value after unindented collection (1) +yaml: | + collection: + key: + - a + - b + - c + foo: bar +php: | + array('collection' => array('key' => array('a', 'b', 'c')), 'foo' => 'bar') +--- +test: Key/value after unindented collection (at the same level) +brief: > + Key/value after unindented collection +yaml: | + collection: + key: + - a + - b + - c + foo: bar +php: | + array('collection' => array('key' => array('a', 'b', 'c'), 'foo' => 'bar')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php new file mode 100644 index 000000000..dc497ca21 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Inline; + +class InlineTest extends \PHPUnit_Framework_TestCase +{ + public function testParse() + { + foreach ($this->getTestsForParse() as $yaml => $value) { + $this->assertSame($value, Inline::parse($yaml), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml)); + } + } + + public function testDump() + { + $testsForDump = $this->getTestsForDump(); + + foreach ($testsForDump as $yaml => $value) { + $this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml)); + } + + foreach ($this->getTestsForParse() as $value) { + $this->assertEquals($value, Inline::parse(Inline::dump($value)), 'check consistency'); + } + + foreach ($testsForDump as $value) { + $this->assertEquals($value, Inline::parse(Inline::dump($value)), 'check consistency'); + } + } + + public function testDumpNumericValueWithLocale() + { + $locale = setlocale(LC_NUMERIC, 0); + if (false === $locale) { + $this->markTestSkipped('Your platform does not support locales.'); + } + + $required_locales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252'); + if (false === setlocale(LC_ALL, $required_locales)) { + $this->markTestSkipped('Could not set any of required locales: '.implode(", ", $required_locales)); + } + + $this->assertEquals('1.2', Inline::dump(1.2)); + $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0))); + + setlocale(LC_ALL, $locale); + } + + public function testHashStringsResemblingExponentialNumericsShouldNotBeChangedToINF() + { + $value = '686e444'; + + $this->assertSame($value, Inline::parse(Inline::dump($value))); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testParseScalarWithIncorrectlyQuotedStringShouldThrowException() + { + $value = "'don't do somthin' like that'"; + Inline::parse($value); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testParseScalarWithIncorrectlyDoubleQuotedStringShouldThrowException() + { + $value = '"don"t do somthin" like that"'; + Inline::parse($value); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testParseInvalidMappingKeyShouldThrowException() + { + $value = '{ "foo " bar": "bar" }'; + Inline::parse($value); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testParseInvalidMappingShouldThrowException() + { + Inline::parse('[foo] bar'); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testParseInvalidSequenceShouldThrowException() + { + Inline::parse('{ foo: bar } bar'); + } + + public function testParseScalarWithCorrectlyQuotedStringShouldReturnString() + { + $value = "'don''t do somthin'' like that'"; + $expect = "don't do somthin' like that"; + + $this->assertSame($expect, Inline::parseScalar($value)); + } + + protected function getTestsForParse() + { + return array( + '' => '', + 'null' => null, + 'false' => false, + 'true' => true, + '12' => 12, + '-12' => -12, + '"quoted string"' => 'quoted string', + "'quoted string'" => 'quoted string', + '12.30e+02' => 12.30e+02, + '0x4D2' => 0x4D2, + '02333' => 02333, + '.Inf' => -log(0), + '-.Inf' => log(0), + "'686e444'" => '686e444', + '686e444' => 646e444, + '123456789123456789123456789123456789' => '123456789123456789123456789123456789', + '"foo\r\nbar"' => "foo\r\nbar", + "'foo#bar'" => 'foo#bar', + "'foo # bar'" => 'foo # bar', + "'#cfcfcf'" => '#cfcfcf', + '::form_base.html.twig' => '::form_base.html.twig', + + '2007-10-30' => mktime(0, 0, 0, 10, 30, 2007), + '2007-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 2007), + '2007-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 2007), + '1960-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 1960), + '1730-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 1730), + + '"a \\"string\\" with \'quoted strings inside\'"' => 'a "string" with \'quoted strings inside\'', + "'a \"string\" with ''quoted strings inside'''" => 'a "string" with \'quoted strings inside\'', + + // sequences + // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon + '[foo, http://urls.are/no/mappings, false, null, 12]' => array('foo', 'http://urls.are/no/mappings', false, null, 12), + '[ foo , bar , false , null , 12 ]' => array('foo', 'bar', false, null, 12), + '[\'foo,bar\', \'foo bar\']' => array('foo,bar', 'foo bar'), + + // mappings + '{foo:bar,bar:foo,false:false,null:null,integer:12}' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), + '{ foo : bar, bar : foo, false : false, null : null, integer : 12 }' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), + '{foo: \'bar\', bar: \'foo: bar\'}' => array('foo' => 'bar', 'bar' => 'foo: bar'), + '{\'foo\': \'bar\', "bar": \'foo: bar\'}' => array('foo' => 'bar', 'bar' => 'foo: bar'), + '{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}' => array('foo\'' => 'bar', "bar\"" => 'foo: bar'), + '{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}' => array('foo: ' => 'bar', "bar: " => 'foo: bar'), + + // nested sequences and mappings + '[foo, [bar, foo]]' => array('foo', array('bar', 'foo')), + '[foo, {bar: foo}]' => array('foo', array('bar' => 'foo')), + '{ foo: {bar: foo} }' => array('foo' => array('bar' => 'foo')), + '{ foo: [bar, foo] }' => array('foo' => array('bar', 'foo')), + + '[ foo, [ bar, foo ] ]' => array('foo', array('bar', 'foo')), + + '[{ foo: {bar: foo} }]' => array(array('foo' => array('bar' => 'foo'))), + + '[foo, [bar, [foo, [bar, foo]], foo]]' => array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo')), + + '[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]' => array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo'))), + + '[foo, bar: { foo: bar }]' => array('foo', '1' => array('bar' => array('foo' => 'bar'))), + '[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',), + ); + } + + protected function getTestsForDump() + { + return array( + 'null' => null, + 'false' => false, + 'true' => true, + '12' => 12, + "'quoted string'" => 'quoted string', + '12.30e+02' => 12.30e+02, + '1234' => 0x4D2, + '1243' => 02333, + '.Inf' => -log(0), + '-.Inf' => log(0), + "'686e444'" => '686e444', + '"foo\r\nbar"' => "foo\r\nbar", + "'foo#bar'" => 'foo#bar', + "'foo # bar'" => 'foo # bar', + "'#cfcfcf'" => '#cfcfcf', + + "'a \"string\" with ''quoted strings inside'''" => 'a "string" with \'quoted strings inside\'', + + "'-dash'" => '-dash', + "'-'" => '-', + + // sequences + '[foo, bar, false, null, 12]' => array('foo', 'bar', false, null, 12), + '[\'foo,bar\', \'foo bar\']' => array('foo,bar', 'foo bar'), + + // mappings + '{ foo: bar, bar: foo, \'false\': false, \'null\': null, integer: 12 }' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), + '{ foo: bar, bar: \'foo: bar\' }' => array('foo' => 'bar', 'bar' => 'foo: bar'), + + // nested sequences and mappings + '[foo, [bar, foo]]' => array('foo', array('bar', 'foo')), + + '[foo, [bar, [foo, [bar, foo]], foo]]' => array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo')), + + '{ foo: { bar: foo } }' => array('foo' => array('bar' => 'foo')), + + '[foo, { bar: foo }]' => array('foo', array('bar' => 'foo')), + + '[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]' => array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo'))), + + '[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',), + ); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php new file mode 100644 index 000000000..289965e8d --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Yaml; + +class ParseExceptionTest extends \PHPUnit_Framework_TestCase +{ + public function testGetMessage() + { + $exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml'); + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")'; + } else { + $message = 'Error message in "\\/var\\/www\\/app\\/config.yml" at line 42 (near "foo: bar")'; + } + + $this->assertEquals($message, $exception->getMessage()); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php new file mode 100644 index 000000000..07e6222d7 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php @@ -0,0 +1,619 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Yaml; +use Symfony\Component\Yaml\Parser; + +class ParserTest extends \PHPUnit_Framework_TestCase +{ + protected $parser; + + protected function setUp() + { + $this->parser = new Parser(); + } + + protected function tearDown() + { + $this->parser = null; + } + + /** + * @dataProvider getDataFormSpecifications + */ + public function testSpecifications($file, $expected, $yaml, $comment) + { + if ('escapedCharacters' == $file) { + if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) { + $this->markTestSkipped('The iconv and mbstring extensions are not available.'); + } + } + + $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); + } + + public function getDataFormSpecifications() + { + $parser = new Parser(); + $path = __DIR__.'/Fixtures'; + + $tests = array(); + $files = $parser->parse(file_get_contents($path.'/index.yml')); + foreach ($files as $file) { + $yamls = file_get_contents($path.'/'.$file.'.yml'); + + // split YAMLs documents + foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { + if (!$yaml) { + continue; + } + + $test = $parser->parse($yaml); + if (isset($test['todo']) && $test['todo']) { + // TODO + } else { + eval('$expected = '.trim($test['php']).';'); + + $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']); + } + } + } + + return $tests; + } + + public function testTabsInYaml() + { + // test tabs in YAML + $yamls = array( + "foo:\n bar", + "foo:\n bar", + "foo:\n bar", + "foo:\n bar", + ); + + foreach ($yamls as $yaml) { + try { + $content = $this->parser->parse($yaml); + + $this->fail('YAML files must not contain tabs'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs'); + $this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs'); + } + } + } + + public function testEndOfTheDocumentMarker() + { + $yaml = <<assertEquals('foo', $this->parser->parse($yaml)); + } + + public function getBlockChompingTests() + { + $tests = array(); + + $yaml = <<<'EOF' +foo: |- + one + two +bar: |- + one + two + +EOF; + $expected = array( + 'foo' => "one\ntwo", + 'bar' => "one\ntwo", + ); + $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: |- + one + two + +bar: |- + one + two + + +EOF; + $expected = array( + 'foo' => "one\ntwo", + 'bar' => "one\ntwo", + ); + $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: |- + one + two +bar: |- + one + two +EOF; + $expected = array( + 'foo' => "one\ntwo", + 'bar' => "one\ntwo", + ); + $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: | + one + two +bar: | + one + two + +EOF; + $expected = array( + 'foo' => "one\ntwo\n", + 'bar' => "one\ntwo\n", + ); + $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: | + one + two + +bar: | + one + two + + +EOF; + $expected = array( + 'foo' => "one\ntwo\n", + 'bar' => "one\ntwo\n", + ); + $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: | + one + two +bar: | + one + two +EOF; + $expected = array( + 'foo' => "one\ntwo\n", + 'bar' => "one\ntwo", + ); + $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: |+ + one + two +bar: |+ + one + two + +EOF; + $expected = array( + 'foo' => "one\ntwo\n", + 'bar' => "one\ntwo\n", + ); + $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: |+ + one + two + +bar: |+ + one + two + + +EOF; + $expected = array( + 'foo' => "one\ntwo\n\n", + 'bar' => "one\ntwo\n\n", + ); + $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: |+ + one + two +bar: |+ + one + two +EOF; + $expected = array( + 'foo' => "one\ntwo\n", + 'bar' => "one\ntwo", + ); + $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >- + one + two +bar: >- + one + two + +EOF; + $expected = array( + 'foo' => "one two", + 'bar' => "one two", + ); + $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >- + one + two + +bar: >- + one + two + + +EOF; + $expected = array( + 'foo' => "one two", + 'bar' => "one two", + ); + $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >- + one + two +bar: >- + one + two +EOF; + $expected = array( + 'foo' => "one two", + 'bar' => "one two", + ); + $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: > + one + two +bar: > + one + two + +EOF; + $expected = array( + 'foo' => "one two\n", + 'bar' => "one two\n", + ); + $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: > + one + two + +bar: > + one + two + + +EOF; + $expected = array( + 'foo' => "one two\n", + 'bar' => "one two\n", + ); + $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: > + one + two +bar: > + one + two +EOF; + $expected = array( + 'foo' => "one two\n", + 'bar' => "one two", + ); + $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >+ + one + two +bar: >+ + one + two + +EOF; + $expected = array( + 'foo' => "one two\n", + 'bar' => "one two\n", + ); + $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >+ + one + two + +bar: >+ + one + two + + +EOF; + $expected = array( + 'foo' => "one two\n\n", + 'bar' => "one two\n\n", + ); + $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml); + + $yaml = <<<'EOF' +foo: >+ + one + two +bar: >+ + one + two +EOF; + $expected = array( + 'foo' => "one two\n", + 'bar' => "one two", + ); + $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml); + + return $tests; + } + + /** + * @dataProvider getBlockChompingTests + */ + public function testBlockChomping($expected, $yaml) + { + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + /** + * Regression test for issue #7989. + * + * @see https://github.com/symfony/symfony/issues/7989 + */ + public function testBlockLiteralWithLeadingNewlines() + { + $yaml = <<<'EOF' +foo: |- + + + bar + +EOF; + $expected = array( + 'foo' => "\n\nbar" + ); + + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + public function testObjectSupportEnabled() + { + $input = <<assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); + } + + public function testObjectSupportDisabledButNoExceptions() + { + $input = <<assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects'); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testObjectsSupportDisabledWithExceptions() + { + $this->parser->parse('foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}', true, false); + } + + public function testNonUtf8Exception() + { + if (!function_exists('mb_detect_encoding') || !function_exists('iconv')) { + $this->markTestSkipped('Exceptions for non-utf8 charsets require the mb_detect_encoding() and iconv() functions.'); + + return; + } + + $yamls = array( + iconv("UTF-8", "ISO-8859-1", "foo: 'äöüß'"), + iconv("UTF-8", "ISO-8859-15", "euro: '€'"), + iconv("UTF-8", "CP1252", "cp1252: '©ÉÇáñ'") + ); + + foreach ($yamls as $yaml) { + try { + $this->parser->parse($yaml); + + $this->fail('charsets other than UTF-8 are rejected.'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.'); + } + } + } + + /** + * + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + * + */ + public function testUnindentedCollectionException() + { + $yaml = <<parser->parse($yaml); + } + + /** + * @expectedException \Symfony\Component\Yaml\Exception\ParseException + */ + public function testSequenceInAMapping() + { + Yaml::parse(<<assertEquals(array('hash' => null), Yaml::parse($input)); + } + + public function testStringBlockWithComments() + { + $this->assertEquals(array('content' => << +

    title

    + + +footer # comment3 +EOT + ), Yaml::parse(<< +

    title

    + + + footer # comment3 +EOF + )); + } + + public function testFoldedStringBlockWithComments() + { + $this->assertEquals(array(array('content' => << +

    title

    + + +footer # comment3 +EOT + )), Yaml::parse(<< +

    title

    + + + footer # comment3 +EOF + )); + } + + public function testNestedFoldedStringBlockWithComments() + { + $this->assertEquals(array(array( + 'title' => 'some title', + 'content' => << +

    title

    + + +footer # comment3 +EOT + )), Yaml::parse(<< +

    title

    + + + footer # comment3 +EOF + )); + } +} + +class B +{ + public $b = 'foo'; +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php new file mode 100644 index 000000000..633978d63 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml\Tests; + +use Symfony\Component\Yaml\Yaml; + +class YamlTest extends \PHPUnit_Framework_TestCase +{ + public function testParseAndDump() + { + $data = array('lorem' => 'ipsum', 'dolor' => 'sit'); + $yml = Yaml::dump($data); + $parsed = Yaml::parse($yml); + $this->assertEquals($data, $parsed); + + $filename = __DIR__.'/Fixtures/index.yml'; + $contents = file_get_contents($filename); + $parsedByFilename = Yaml::parse($filename); + $parsedByContents = Yaml::parse($contents); + $this->assertEquals($parsedByFilename, $parsedByContents); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php new file mode 100644 index 000000000..1b8eeed57 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +/** + * Unescaper encapsulates unescaping rules for single and double-quoted + * YAML strings. + * + * @author Matthew Lewinski + */ +class Unescaper +{ + // Parser and Inline assume UTF-8 encoding, so escaped Unicode characters + // must be converted to that encoding. + const ENCODING = 'UTF-8'; + + // Regex fragment that matches an escaped character in a double quoted + // string. + const REGEX_ESCAPED_CHARACTER = "\\\\([0abt\tnvfre \\\"\\/\\\\N_LP]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})"; + + /** + * Unescapes a single quoted string. + * + * @param string $value A single quoted string. + * + * @return string The unescaped string. + */ + public function unescapeSingleQuotedString($value) + { + return str_replace('\'\'', '\'', $value); + } + + /** + * Unescapes a double quoted string. + * + * @param string $value A double quoted string. + * + * @return string The unescaped string. + */ + public function unescapeDoubleQuotedString($value) + { + $self = $this; + $callback = function ($match) use ($self) { + return $self->unescapeCharacter($match[0]); + }; + + // evaluate the string + return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); + } + + /** + * Unescapes a character that was found in a double-quoted string + * + * @param string $value An escaped character + * + * @return string The unescaped character + */ + public function unescapeCharacter($value) + { + switch ($value{1}) { + case '0': + return "\x0"; + case 'a': + return "\x7"; + case 'b': + return "\x8"; + case 't': + return "\t"; + case "\t": + return "\t"; + case 'n': + return "\n"; + case 'v': + return "\xb"; + case 'f': + return "\xc"; + case 'r': + return "\xd"; + case 'e': + return "\x1b"; + case ' ': + return ' '; + case '"': + return '"'; + case '/': + return '/'; + case '\\': + return '\\'; + case 'N': + // U+0085 NEXT LINE + return $this->convertEncoding("\x00\x85", self::ENCODING, 'UCS-2BE'); + case '_': + // U+00A0 NO-BREAK SPACE + return $this->convertEncoding("\x00\xA0", self::ENCODING, 'UCS-2BE'); + case 'L': + // U+2028 LINE SEPARATOR + return $this->convertEncoding("\x20\x28", self::ENCODING, 'UCS-2BE'); + case 'P': + // U+2029 PARAGRAPH SEPARATOR + return $this->convertEncoding("\x20\x29", self::ENCODING, 'UCS-2BE'); + case 'x': + $char = pack('n', hexdec(substr($value, 2, 2))); + + return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE'); + case 'u': + $char = pack('n', hexdec(substr($value, 2, 4))); + + return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE'); + case 'U': + $char = pack('N', hexdec(substr($value, 2, 8))); + + return $this->convertEncoding($char, self::ENCODING, 'UCS-4BE'); + } + } + + /** + * Convert a string from one encoding to another. + * + * @param string $value The string to convert + * @param string $to The input encoding + * @param string $from The output encoding + * + * @return string The string with the new encoding + * + * @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring) + */ + private function convertEncoding($value, $to, $from) + { + if (function_exists('mb_convert_encoding')) { + return mb_convert_encoding($value, $to, $from); + } elseif (function_exists('iconv')) { + return iconv($from, $to, $value); + } + + throw new \RuntimeException('No suitable convert encoding function (install the iconv or mbstring extension).'); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php new file mode 100644 index 000000000..61793fb3a --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Yaml; + +use Symfony\Component\Yaml\Exception\ParseException; + +/** + * Yaml offers convenience methods to load and dump YAML. + * + * @author Fabien Potencier + * + * @api + */ +class Yaml +{ + /** + * Parses YAML into a PHP array. + * + * The parse method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. + * + * Usage: + * + * $array = Yaml::parse('config.yml'); + * print_r($array); + * + * + * As this method accepts both plain strings and file names as an input, + * you must validate the input before calling this method. Passing a file + * as an input is a deprecated feature and will be removed in 3.0. + * + * @param string $input Path to a YAML file or a string containing YAML + * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise + * @param bool $objectSupport True if object support is enabled, false otherwise + * + * @return array The YAML converted to a PHP array + * + * @throws ParseException If the YAML is not valid + * + * @api + */ + public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false) + { + // if input is a file, process it + $file = ''; + if (strpos($input, "\n") === false && is_file($input)) { + if (false === is_readable($input)) { + throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input)); + } + + $file = $input; + $input = file_get_contents($file); + } + + $yaml = new Parser(); + + try { + return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport); + } catch (ParseException $e) { + if ($file) { + $e->setParsedFile($file); + } + + throw $e; + } + } + + /** + * Dumps a PHP array to a YAML string. + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. + * + * @param array $array PHP array + * @param int $inline The level where you switch to inline YAML + * @param int $indent The amount of spaces to use for indentation of nested nodes. + * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise + * @param bool $objectSupport true if object support is enabled, false otherwise + * + * @return string A YAML string representing the original PHP array + * + * @api + */ + public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) + { + $yaml = new Dumper(); + $yaml->setIndentation($indent); + + return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport); + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json b/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json new file mode 100644 index 000000000..33c298535 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/yaml", + "type": "library", + "description": "Symfony Yaml Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Yaml\\": "" } + }, + "target-dir": "Symfony/Component/Yaml", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist b/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist new file mode 100644 index 000000000..aa77e9de7 --- /dev/null +++ b/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./vendor + ./Tests + + + + From de611f52dc02369b7908490303c961965343c9c7 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:48:16 -0400 Subject: [PATCH 071/166] updating composer's autoload --- composer.json | 4 +- composer.lock | 102 ++++++++++++++++++++- core/lib/autoload.php | 2 +- core/lib/composer/autoload_namespaces.php | 2 + core/lib/composer/autoload_real.php | 8 +- core/lib/composer/installed.json | 104 ++++++++++++++++++++++ 6 files changed, 215 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 1c21acf24..3e52e19e2 100644 --- a/composer.json +++ b/composer.json @@ -8,6 +8,8 @@ } }, "require": { - "alchemy/zippy": "0.2.0" + "alchemy/zippy": "0.2.0", + "michelf/php-markdown": "1.4", + "symfony/yaml": "2.4.4" } } diff --git a/composer.lock b/composer.lock index 35339bfe5..e4f4337ae 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "847848f40fffd1dc29a257597bdc2576", + "hash": "27deb92c30ef38ddb06e420b9f908f1c", "packages": [ { "name": "alchemy/zippy", @@ -227,6 +227,57 @@ ], "time": "2014-05-07 17:04:22" }, + { + "name": "michelf/php-markdown", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/michelf/php-markdown.git", + "reference": "96d8150406f67e683ef4acc09fef91785fef1266" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/michelf/php-markdown/zipball/96d8150406f67e683ef4acc09fef91785fef1266", + "reference": "96d8150406f67e683ef4acc09fef91785fef1266", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-lib": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Michelf": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "http://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "http://daringfireball.net/" + } + ], + "description": "PHP Markdown", + "homepage": "http://michelf.ca/projects/php-markdown/", + "keywords": [ + "markdown" + ], + "time": "2013-11-29 17:09:24" + }, { "name": "pimple/pimple", "version": "v1.1.1", @@ -428,6 +479,55 @@ "description": "Symfony Process Component", "homepage": "http://symfony.com", "time": "2014-04-27 13:34:57" + }, + { + "name": "symfony/yaml", + "version": "v2.4.4", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/65539ecde838f9c0d18b006b2101e3deb4b5c9ff", + "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-04-18 20:37:09" } ], "packages-dev": [ diff --git a/core/lib/autoload.php b/core/lib/autoload.php index e972d4b14..cbe1894d6 100644 --- a/core/lib/autoload.php +++ b/core/lib/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit140ff88b0c869590677b7653c76d6c6d::getLoader(); +return ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf::getLoader(); diff --git a/core/lib/composer/autoload_namespaces.php b/core/lib/composer/autoload_namespaces.php index 1ee2efd18..9dce742c5 100644 --- a/core/lib/composer/autoload_namespaces.php +++ b/core/lib/composer/autoload_namespaces.php @@ -6,10 +6,12 @@ $baseDir = dirname(dirname($vendorDir)); return array( + 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Pimple' => array($vendorDir . '/pimple/pimple/lib'), + 'Michelf' => array($vendorDir . '/michelf/php-markdown'), 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), diff --git a/core/lib/composer/autoload_real.php b/core/lib/composer/autoload_real.php index 60c4b1e76..67493fbbf 100644 --- a/core/lib/composer/autoload_real.php +++ b/core/lib/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit140ff88b0c869590677b7653c76d6c6d +class ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit140ff88b0c869590677b7653c76d6c6d', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit140ff88b0c869590677b7653c76d6c6d', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf', 'loadClassLoader')); $vendorDir = dirname(__DIR__); $baseDir = dirname(dirname($vendorDir)); @@ -47,7 +47,7 @@ public static function getLoader() } } -function composerRequire140ff88b0c869590677b7653c76d6c6d($file) +function composerRequire6d5344d88a6339674b66b055e9491daf($file) { require $file; } diff --git a/core/lib/composer/installed.json b/core/lib/composer/installed.json index ef961958c..41173b449 100644 --- a/core/lib/composer/installed.json +++ b/core/lib/composer/installed.json @@ -436,5 +436,109 @@ "tar", "zip" ] + }, + { + "name": "michelf/php-markdown", + "version": "1.4.0", + "version_normalized": "1.4.0.0", + "source": { + "type": "git", + "url": "https://github.com/michelf/php-markdown.git", + "reference": "96d8150406f67e683ef4acc09fef91785fef1266" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/michelf/php-markdown/zipball/96d8150406f67e683ef4acc09fef91785fef1266", + "reference": "96d8150406f67e683ef4acc09fef91785fef1266", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2013-11-29 17:09:24", + "type": "library", + "extra": { + "branch-alias": { + "dev-lib": "1.4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Michelf": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "http://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "http://daringfireball.net/" + } + ], + "description": "PHP Markdown", + "homepage": "http://michelf.ca/projects/php-markdown/", + "keywords": [ + "markdown" + ] + }, + { + "name": "symfony/yaml", + "version": "v2.4.4", + "version_normalized": "2.4.4.0", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/65539ecde838f9c0d18b006b2101e3deb4b5c9ff", + "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2014-04-18 20:37:09", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com" } ] From 2fa240aa0fe6e3b87e0ef4179feae8f6aadf3e3d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:48:39 -0400 Subject: [PATCH 072/166] lib to parse markdown with yaml front matter --- core/lib/PatternLab/DocumentationParser.php | 98 +++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 core/lib/PatternLab/DocumentationParser.php diff --git a/core/lib/PatternLab/DocumentationParser.php b/core/lib/PatternLab/DocumentationParser.php new file mode 100644 index 000000000..770cb8da3 --- /dev/null +++ b/core/lib/PatternLab/DocumentationParser.php @@ -0,0 +1,98 @@ + Date: Mon, 19 May 2014 16:48:53 -0400 Subject: [PATCH 073/166] standalone JSON library --- core/lib/PatternLab/JSON.php | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 core/lib/PatternLab/JSON.php diff --git a/core/lib/PatternLab/JSON.php b/core/lib/PatternLab/JSON.php new file mode 100644 index 000000000..b824d3e1b --- /dev/null +++ b/core/lib/PatternLab/JSON.php @@ -0,0 +1,51 @@ + false, + JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', + JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', + JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded' + ); + + /** + * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 + * @param {String} the file that generated the error + */ + public static function hasError() { + $error = json_last_error(); + $errorMessage = array_key_exists($error, self::$errors) ? self::$errors[$error] : "Unknown error ({$error})"; + return $errorMessage; + } + + /** + * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 + * @param {String} the file that generated the error + */ + public static function lastErrorMsg($file,$message,$data) { + print "\nThe JSON file, ".$file.", wasn't loaded. The error: ".$message."\n"; + if ($message == "Syntax error, malformed JSON") { + print "\n"; + $parser = new JsonLint\JsonParser(); + $error = $parser->lint($data); + print $error->getMessage(); + print "\n\n"; + } + } + +} \ No newline at end of file From f0821d984f4c23289baf7d58fb67c26be8d1f650 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:49:24 -0400 Subject: [PATCH 074/166] pattern info and pattern info rules standardization --- core/lib/PatternLab/PatternInfo.php | 74 ++++++++ core/lib/PatternLab/PatternInfoRule.php | 70 ++++++++ .../PatternInfoRules/DocumentationRule.php | 90 ++++++++++ .../PatternInfoRules/PatternRule.php | 135 +++++++++++++++ .../PatternInfoRules/PatternSubtypeRule.php | 62 +++++++ .../PatternInfoRules/PatternTypeRule.php | 66 ++++++++ .../PatternInfoRules/PseudoPatternRule.php | 160 ++++++++++++++++++ 7 files changed, 657 insertions(+) create mode 100644 core/lib/PatternLab/PatternInfo.php create mode 100644 core/lib/PatternLab/PatternInfoRule.php create mode 100644 core/lib/PatternLab/PatternInfoRules/DocumentationRule.php create mode 100644 core/lib/PatternLab/PatternInfoRules/PatternRule.php create mode 100644 core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php create mode 100644 core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php create mode 100644 core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php diff --git a/core/lib/PatternLab/PatternInfo.php b/core/lib/PatternLab/PatternInfo.php new file mode 100644 index 000000000..fd7222767 --- /dev/null +++ b/core/lib/PatternLab/PatternInfo.php @@ -0,0 +1,74 @@ +setFlags(\FilesystemIterator::SKIP_DOTS); + + $patternObjects = iterator_to_array($patternObjects); + ksort($patternObjects); + + self::$d["link"] = array(); + self::$navItems["patternTypes"] = array(); + + foreach ($patternObjects as $name => $object) { + + $ext = $object->getExtension(); + $isDir = $object->isDir(); + $isFile = $object->isFile(); + $path = $object->getPath(); + $pathName = $object->getPathname(); + $name = $object->getFilename(); + + $depth = substr_count(str_replace($options["patternSourceDir"],"",$pathName),DIRECTORY_SEPARATOR); + + foreach (self::$rules as $rule) { + if ($rule->testRule($depth, $ext, $isDir, $isFile, $name)) { + $rule->runRule($depth, $ext, $path, $pathName, $name); + } + } + + } + + } + + public static function loadRules($options) { + foreach (glob(__DIR__."/PatternInfoRules/*.php") as $filename) { + $rule = str_replace(".php","",str_replace(__DIR__."/PatternInfoRules/","",$filename)); + $ruleClass = "PatternLab\PatternInfoRules\\".$rule; + self::$rules[] = new $ruleClass($options); + } + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRule.php b/core/lib/PatternLab/PatternInfoRule.php new file mode 100644 index 000000000..44e10eb79 --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRule.php @@ -0,0 +1,70 @@ +patternSourceDir = $options["patternSourceDir"]; + + } + + public function testRule($depth, $ext, $isDir, $isFile, $name) { + + if (($this->depthProp != 3) && ($depth != $this->depthProp)) { + return false; + } + + if (($ext == $this->extProp) && ($isDir == $this->isDirProp) && ($isFile == $this->isFileProp)) { + if ($this->searchProp != "") { + return (strpos($name,$this->searchProp) !== false) ? true : false; + } else { + return true; + } + } + + return false; + + } + + /** + * Get the name for a given pattern sans any possible digits used for reordering + * @param {String} the pattern based on the filesystem name + * @param {Boolean} whether or not to strip slashes from the pattern name + * + * @return {String} a lower-cased version of the pattern name + */ + protected function getPatternName($pattern, $clean = true) { + $patternBits = explode("-",$pattern,2); + $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; + return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; + } + + protected function findKey($haystack,$needle,$attribute) { + $key = false; + foreach ($haystack as $strawKey => $strawValues) { + if ($strawValues[$attribute] == $needle) { + $key = $strawKey; + break; + } + } + return $key; + } +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php b/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php new file mode 100644 index 000000000..68e89d585 --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php @@ -0,0 +1,90 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "md"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ""; + + } + + public function runRule($depth, $ext, $path, $pathName, $name) { + + $bi = PatternInfo::$bi; + $ni = PatternInfo::$ni; + $patternSubtype = PatternInfo::$patternSubtype; + $patternTypeDash = PatternInfo::$patternTypeDash; + + $patternFull = $name; // 00-colors.md + $pattern = str_replace(".".$this->extProp,"",$patternFull); // 00-colors + + // make sure the pattern isn't hidden + if ($patternFull[0] != "_") { + + // parse data + $text = file_get_contents($pathName); + list($yaml,$markdown) = DocumentationParser::parse($text); + + if ($depth == 1) { + + // add to pattern subtype + if (isset($yaml["title"])) { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeLC"] = strtolower($yaml["title"]); + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeUC"] = ucwords($yaml["title"]); + unset($yaml["title"]); + } + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeDesc"] = $markdown; + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeMeta"] = $yaml; + + } else if ($depth == 2) { + + // get base info + $patternDash = $this->getPatternName($pattern,false); // colors + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors + + // see if the pattern is already part of the nav + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); + if ($key === false) { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = array(); + $a = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"]; + end($a); + $key = key($a); + } + + // add to the pattern + if (isset($yaml["title"])) { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"] = ucwords($yaml["title"]); + unset($yaml["title"]); + } + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternDesc"] = $markdown; + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternMeta"] = $yaml; + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternPartial"] = $patternPartial; + + } + + } + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternRule.php b/core/lib/PatternLab/PatternInfoRules/PatternRule.php new file mode 100644 index 000000000..d4598b7ef --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRules/PatternRule.php @@ -0,0 +1,135 @@ +patternExtension = $options["patternExtension"]; + + $this->depthProp = 3; // 3 means that depth won't be checked + $this->extProp = $this->patternExtension; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ""; + + } + + public function runRule($depth, $ext, $path, $pathName, $name) { + + $bi = PatternInfo::$bi; + $ni = PatternInfo::$ni; + $patternSubtype = PatternInfo::$patternSubtype; + $patternSubtypeDash = PatternInfo::$patternSubtypeDash; + $patternSubtypeSet = PatternInfo::$patternSubtypeSet; + $patternType = PatternInfo::$patternType; + $patternTypeDash = PatternInfo::$patternTypeDash; + $dirSep = DIRECTORY_SEPARATOR; + + $patternFull = $name; // 00-colors.mustache + $pattern = str_replace(".".$this->patternExtension,"",$patternFull); // 00-colors + + // check for pattern state + $patternState = ""; + if (strpos($pattern,"@") !== false) { + $patternBits = explode("@",$pattern,2); + $pattern = $patternBits[0]; + $patternState = $patternBits[1]; + } + + if ($patternSubtypeSet) { + $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) + } else { + $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) + } + + // track to see if this pattern should get rendered + $render = false; + + // make sure the pattern isn't hidden + if ($patternFull[0] != "_") { + + // set-up the names + $patternDash = $this->getPatternName($pattern,false); // colors + $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors + + // see if the pattern name is already set via .md file + $patternName = ucwords($patternClean); + if ($depth == 2) { + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); + if ($key !== false) { + $patternName = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"]; + } + } + + // set-up the info for the nav + $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", + "patternSrcPath" => str_replace($this->patternSourceDir,"",$pathName), + "patternName" => $patternName, + "patternState" => $patternState, + "patternPartial" => $patternPartial); + + // add to the nav + if ($depth == 1) { + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"],$patternPartial,"patternPartial"); + if ($key !== false) { + PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key], $patternInfo); + } else { + PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; + } + } else { + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); + if ($key !== false) { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key], $patternInfo); + } else { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; + } + } + + // add to the link var for inclusion in patterns + PatternInfo::$d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; + + // yup, this pattern should get rendered + $render = true; + + } else { + + // replace the underscore to generate a good file pattern name + $patternDash = $this->getPatternName(str_replace("_","",$pattern),false); // colors + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors + + } + + // add all patterns to patternPaths + $patternSrcPath = str_replace($this->patternSourceDir,"",str_replace(".".$this->patternExtension,"",$pathName)); + $patternDestPath = $patternPathDash; + PatternInfo::$patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, + "patternDestPath" => $patternDestPath, + "patternPartial" => $patternPartial, + "patternState" => $patternState, + "patternType" => $patternTypeDash, + "render" => $render); + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php b/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php new file mode 100644 index 000000000..c66ee8ebd --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php @@ -0,0 +1,62 @@ +depthProp = 1; + $this->extProp = ""; + $this->isDirProp = true; + $this->isFileProp = false; + $this->searchProp = ""; + + } + + public function runRule($depth, $ext, $path, $pathName, $name) { + + // is this the first bucket to be set? + PatternInfo::$ni = (!PatternInfo::$patternSubtypeSet) ? 0 : PatternInfo::$ni + 1; + $ni = PatternInfo::$ni; + $bi = PatternInfo::$bi; + $patternTypeDash = PatternInfo::$patternTypeDash; + + // set-up the names + $patternSubtype = $name; // 02-blocks + $patternSubtypeDash = $this->getPatternName($name,false); // blocks + $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) + + // add to patternPartials + PatternInfo::$patternPartials[$patternTypeDash."-".$patternSubtypeDash] = array(); + + // add a new patternSubtype to the nav + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternSubtypeClean), + "patternSubtypeUC" => ucwords($patternSubtypeClean), + "patternSubtype" => $patternSubtype, + "patternSubtypeDash" => $patternSubtypeDash, + "patternSubtypeItems" => array()); + + // starting a new set of pattern types. it might not have any pattern subtypes + PatternInfo::$patternSubtype = $patternSubtype; + PatternInfo::$patternSubtypeDash = $patternSubtypeDash; + PatternInfo::$patternSubtypeSet = true; + + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php b/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php new file mode 100644 index 000000000..b12e7e073 --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php @@ -0,0 +1,66 @@ +depthProp = 0; + $this->extProp = ""; + $this->isDirProp = true; + $this->isFileProp = false; + $this->searchProp = ""; + + } + + public function runRule($depth, $ext, $path, $pathName, $name) { + + PatternInfo::$bi = (count(PatternInfo::$navItems["patternTypes"]) == 0) ? 0 : PatternInfo::$bi + 1; + $bi = PatternInfo::$bi; + + // set-up the names + $patternType = $name; // 00-atoms + $patternTypeDash = $this->getPatternName($name,false); // atoms + $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) + + // add to pattern types & pattern paths + $patternTypes[] = $patternType; + $patternPaths[$patternTypeDash] = array(); + + // add a new patternType to the nav + PatternInfo::$navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternTypeClean), + "patternTypeUC" => ucwords($patternTypeClean), + "patternType" => $patternType, + "patternTypeDash" => $patternTypeDash, + "patternTypeItems" => array(), + "patternItems" => array()); + + // starting a new set of pattern types. it might not have any pattern subtypes + PatternInfo::$patternSubtypeSet = false; + PatternInfo::$patternType = $patternType; + PatternInfo::$patternTypeDash = $patternTypeDash; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php b/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php new file mode 100644 index 000000000..5335d0f2e --- /dev/null +++ b/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php @@ -0,0 +1,160 @@ +patternExtension = $options["patternExtension"]; + + $this->depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "json"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = "~"; + + } + + public function runRule($depth, $ext, $path, $pathName, $name) { + + $bi = PatternInfo::$bi; + $ni = PatternInfo::$ni; + $patternSubtype = PatternInfo::$patternSubtype; + $patternSubtypeDash = PatternInfo::$patternSubtypeDash; + $patternSubtypeSet = PatternInfo::$patternSubtypeSet; + $patternType = PatternInfo::$patternType; + $patternTypeDash = PatternInfo::$patternTypeDash; + $dirSep = DIRECTORY_SEPARATOR; + + $patternSubtypeInclude = ($patternSubtypeSet) ? $patternSubtype."-" : ""; + $patternFull = $name; + + if ($patternFull[0] != "_") { + + // check for a pattern state + $patternState = ""; + $patternBits = explode("@",$patternFull,2); + if (isset($patternBits[1])) { + $patternState = str_replace(".json","",$patternBits[1]); + $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); + } + + // set-up the names + // $patternFull is defined above 00-colors.mustache + $patternBits = explode("~",$patternFull); + $patternBase = $patternBits[0].".".$this->patternExtension; // 00-homepage.mustache + $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage + $patternBaseJSON = $patternBits[0].".json"; // 00-homepage.json + $stripJSON = str_replace(".json","",$patternBits[1]); + $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); + $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency + $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency + $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency + $patternClean = str_replace("-"," ",$patternDash); // homepage emergency + $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency + + // add to patternPaths + if ($patternSubtypeSet) { + $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) + } else { + $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) + } + + // add all patterns to patternPaths + $patternSrcPath = PatternInfo::$patternPaths[$patternTypeDash][$patternBaseDash]["patternSrcPath"]; + $patternDestPath = $patternPathDash; + PatternInfo::$patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, + "patternDestPath" => $patternDestPath, + "patternPartial" => $patternPartial, + "patternState" => $patternState, + "patternType" => $patternTypeDash, + "render" => true); + + // see if the pattern name is already set via .md file + $patternName = ucwords($patternClean); + if ($depth == 2) { + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); + if ($key !== false) { + $patternName = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"]; + } + } + + // set-up the info for the nav + $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", + "patternSrcPath" => str_replace($this->patternSourceDir,"",preg_replace("/\~(.*)\.json/",".".$this->patternExtension,$pathName)), + "patternName" => $patternName, + "patternState" => $patternState, + "patternPartial" => $patternPartial); + + // add to the nav + if ($depth == 1) { + $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"],$patternPartial,"patternPartial"); + if ($key !== false) { + PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key], $patternInfo); + } else { + PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; + } + } else { + if ($key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial")) { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key], $patternInfo); + } else { + PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = $patternInfo; + } + } + + // add to the link var for inclusion in patterns + PatternInfo::$d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; + + // get the base data + $patternDataBase = array(); + if (file_exists($path."/".$patternBaseJSON)) { + $data = file_get_contents($path."/".$patternBaseJSON); + $patternDataBase = json_decode($data,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); + } + } + + // get the special pattern data + $data = file_get_contents($pathName); + $patternData = (array) json_decode($data); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($name,$jsonErrorMessage,$data); + } + + // merge them for the file + if (!isset(PatternInfo::$d["patternSpecific"][$patternPartial])) { + PatternInfo::$d["patternSpecific"][$patternPartial] = array(); + PatternInfo::$d["patternSpecific"][$patternPartial]["data"] = array(); + PatternInfo::$d["patternSpecific"][$patternPartial]["listItems"] = array(); + } + + if (is_array($patternDataBase) && is_array($patternData)) { + PatternInfo::$d["patternSpecific"][$patternPartial]["data"] = array_merge($patternDataBase, $patternData); + } + + } + + } + +} + From 10e205c7bdadc2dfcaae23ff408ac26799651979 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:49:48 -0400 Subject: [PATCH 075/166] removing old pattern info code --- core/lib/PatternLab/Builder.php | 404 +++++--------------------------- 1 file changed, 56 insertions(+), 348 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 81300b109..db3d85caa 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -362,8 +362,8 @@ protected function gatherData() { if (file_exists($this->sd."/_data/_data.json")) { $data = file_get_contents($this->sd."/_data/_data.json"); $this->d = json_decode($data,true); - if ($jsonErrorMessage = $this->jsonHasError()) { - $this->jsonLastErrorMsg("_data/_data.json",$jsonErrorMessage,$data); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg("_data/_data.json",$jsonErrorMessage,$data); } } else { print "Missing a required file, source/_data/_data.json. Aborting.\n"; @@ -504,302 +504,18 @@ protected function gatherPatternInfo() { // make sure the pattern header & footer aren't added $this->addPatternHF = false; - // set-up the defaults - $patternType = ""; - $patternSubtype = ""; - $patternSubtypeSet = false; - $dirSep = DIRECTORY_SEPARATOR; - + // gather pattern info based on the supported rules + $options = array("patternSourceDir" => __DIR__.$this->sp, "patternExtension" => $this->patternExtension); + PatternInfo::loadRules($options); + PatternInfo::gather($options); // initialize various arrays - $this->navItems = array(); - $this->navItems["patternTypes"] = array(); - $this->patternPaths = array(); - $this->patternTypes = array(); - $this->patternLineages = array(); - $this->patternPartials = array(); - $this->viewAllPaths = array(); - - // iterate over the patterns & related data and regenerate the entire site if they've changed - $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__.$this->sp), \RecursiveIteratorIterator::SELF_FIRST); - $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); - - $patternObjects = iterator_to_array($patternObjects); - ksort($patternObjects); - - foreach($patternObjects as $name => $object) { - - $name = str_replace(__DIR__.$this->sp,"",$name); - $depth = substr_count($name,$dirSep); - - // track old types and subtypes for increment purposes - - if ($object->isDir() && ($depth == 0)) { - - /************************************* - * This section is for: - * The pattern type directory - *************************************/ - - // is this the first bucket to be set? - $bi = (count($this->navItems["patternTypes"]) == 0) ? 0 : $bi + 1; - - // set-up the names - $patternType = $name; // 00-atoms - $patternTypeDash = $this->getPatternName($name,false); // atoms - $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) - - // add to pattern types & pattern paths - $this->patternTypes[] = $patternType; - $this->patternPaths[$patternTypeDash] = array(); - - // add a new patternType to the nav - $this->navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternTypeClean), - "patternTypeUC" => ucwords($patternTypeClean), - "patternType" => $patternType, - "patternTypeDash" => $patternTypeDash); - - // starting a new set of pattern types. it might not have any pattern subtypes - $patternSubtypeSet = false; - - } else if ($object->isDir() && ($depth == 1)) { - - /************************************* - * This section is for: - * The pattern sub-type directory - *************************************/ - - // is this the first bucket to be set? - $ni = (!$patternSubtypeSet) ? 0 : $ni + 1; - - // set-up the names - $patternSubtype = $object->getFilename(); // 02-blocks - $patternSubtypeDash = $this->getPatternName($object->getFilename(),false); // blocks - $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) - - // add to patternPartials - $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash] = array(); - - // add a new patternSubtype to the nav - $this->navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternSubtypeClean), - "patternSubtypeUC" => ucwords($patternSubtypeClean), - "patternSubtype" => $patternSubtype, - "patternSubtypeDash" => $patternSubtypeDash); - - // starting a new set of pattern types. it might not have any pattern subtypes - $patternSubtypeSet = true; - - } else if ($object->isFile() && ($object->getExtension() == $this->patternExtension)) { - - /************************************* - * This section is for: - * Mustache patterns - *************************************/ - - $patternFull = $object->getFilename(); // 00-colors.mustache - $pattern = str_replace(".".$this->patternExtension,"",$patternFull); // 00-colors - - // check for pattern state - $patternState = ""; - if (strpos($pattern,"@") !== false) { - $patternBits = explode("@",$pattern,2); - $pattern = $patternBits[0]; - $patternState = $patternBits[1]; - } - - if ($patternSubtypeSet) { - $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - } else { - $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) - } - - // track to see if this pattern should get rendered - $render = false; - - // make sure the pattern isn't hidden - if ($patternFull[0] != "_") { - - // set-up the names - $patternDash = $this->getPatternName($pattern,false); // colors - $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - // set-up the info for the nav - $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", - "patternSrcPath" => str_replace(__DIR__.$this->sp,"",$object->getPathname()), - "patternName" => ucwords($patternClean), - "patternState" => $patternState, - "patternPartial" => $patternPartial); - - // add to the nav - if ($depth == 1) { - $this->navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; - } else { - $this->navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; - } - - // add to the link var for inclusion in patterns - $this->d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; - - // yup, this pattern should get rendered - $render = true; - - } else { - - // replace the underscore to generate a good file pattern name - $patternDash = $this->getPatternName(str_replace("_","",$pattern),false); // colors - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - } - - // add all patterns to patternPaths - $patternSrcPath = str_replace(__DIR__.$this->sp,"",str_replace(".".$this->patternExtension,"",$object->getPathname())); - $patternDestPath = $patternPathDash; - $this->patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, - "patternDestPath" => $patternDestPath, - "patternPartial" => $patternPartial, - "patternState" => $patternState, - "patternType" => $patternTypeDash, - "render" => $render); - - } else if ($object->isFile() && ($object->getExtension() == "json") && (strpos($object->getFilename(),"~") !== false)) { - - /************************************* - * This section is for: - * JSON psuedo-patterns - *************************************/ - - $patternSubtypeInclude = ($patternSubtypeSet) ? $patternSubtype."-" : ""; - $patternFull = $object->getFilename(); - - if ($patternFull[0] != "_") { - - // check for a pattern state - $patternState = ""; - $patternBits = explode("@",$patternFull,2); - if (isset($patternBits[1])) { - $patternState = str_replace(".json","",$patternBits[1]); - $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); - } - - // set-up the names - // $patternFull is defined above 00-colors.mustache - $patternBits = explode("~",$patternFull); - $patternBase = $patternBits[0].".".$this->patternExtension; // 00-homepage.mustache - $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage - $patternBaseJSON = $patternBits[0].".json"; // 00-homepage.json - $stripJSON = str_replace(".json","",$patternBits[1]); - $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); - $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency - $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency - $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency - $patternClean = str_replace("-"," ",$patternDash); // homepage emergency - $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency - - // add to patternPaths - if ($patternSubtypeSet) { - $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - } else { - $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) - } - - // add all patterns to patternPaths - $patternSrcPath = $this->patternPaths[$patternTypeDash][$patternBaseDash]["patternSrcPath"]; - $patternDestPath = $patternPathDash; - $this->patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, - "patternDestPath" => $patternDestPath, - "patternPartial" => $patternPartial, - "patternState" => $patternState, - "patternType" => $patternTypeDash, - "render" => true); - - // set-up the info for the nav - $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", - "patternSrcPath" => str_replace(__DIR__.$this->sp,"",preg_replace("/\~(.*)\.json/",".".$this->patternExtension,$object->getPathname())), - "patternName" => ucwords($patternClean), - "patternState" => $patternState, - "patternPartial" => $patternPartial); - - // add to the nav - if ($depth == 1) { - $this->navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; - } else { - $this->navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; - } - - // add to the link var for inclusion in patterns - $this->d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; - - // get the base data - $patternDataBase = array(); - if (file_exists($object->getPath()."/".$patternBaseJSON)) { - $data = file_get_contents($object->getPath()."/".$patternBaseJSON); - $patternDataBase = json_decode($data,true); - if ($jsonErrorMessage = $this->jsonHasError()) { - $this->jsonLastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); - } - } - - // get the special pattern data - $data = file_get_contents($object->getPathname()); - $patternData = (array) json_decode($data); - if ($jsonErrorMessage = $this->jsonHasError()) { - $this->jsonLastErrorMsg($object->getFilename(),$jsonErrorMessage,$data); - } - - // merge them for the file - if (!isset($this->d["patternSpecific"][$patternPartial])) { - $this->d["patternSpecific"][$patternPartial] = array(); - $this->d["patternSpecific"][$patternPartial]["data"] = array(); - $this->d["patternSpecific"][$patternPartial]["listItems"] = array(); - } - - if (is_array($patternDataBase) && is_array($patternData)) { - $this->d["patternSpecific"][$patternPartial]["data"] = array_merge($patternDataBase, $patternData); - } - - } - - } else if ($object->isFile() && ($object->getExtension() == "json")) { - - /************************************* - * This section is for: - * JSON data - *************************************/ - $patternFull = $object->getFilename(); // 00-colors.mustache - $pattern = str_replace(".listitems","",str_replace(".json","",$patternFull)); // 00-colors - $patternDash = $this->getPatternName($pattern,false); // colors - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - if ($patternFull[0] != "_") { - - if (!isset($this->d["patternSpecific"][$patternPartial])) { - $this->d["patternSpecific"][$patternPartial] = array(); - $this->d["patternSpecific"][$patternPartial]["data"] = array(); - $this->d["patternSpecific"][$patternPartial]["listItems"] = array(); - } - - if (strpos($object->getFilename(),".listitems.json") !== false) { - $patternData = $this->getListItems($object->getPathname()); - $this->d["patternSpecific"][$patternPartial]["listItems"] = $patternData; - } else { - $data = file_get_contents($object->getPathname()); - $patternData = json_decode($data,true); - if ($jsonErrorMessage = $this->jsonHasError()) { - $this->jsonLastErrorMsg($object->getFilename(),$jsonErrorMessage,$data); - } - $this->d["patternSpecific"][$patternPartial]["data"] = $patternData; - } - - } - - } - - } + $this->navItems = PatternInfo::$navItems; + $this->patternPaths = PatternInfo::$patternPaths; + $this->patternTypes = PatternInfo::$patternTypes; + $this->patternLineages = array(); + $this->patternPartials = array(); + $this->viewAllPaths = array(); // get all of the lineages $this->gatherLineages(); @@ -873,14 +589,14 @@ protected function gatherPatternInfo() { $patternTypeDash = $patternTypeValues["patternTypeDash"]; // if this has a second level of patterns check them out (means we don't process pages & templates) - if (isset($patternTypeValues["patternTypeItems"]) && (!in_array($patternType,$this->styleGuideExcludes))) { + if ((count($patternTypeValues["patternTypeItems"]) != 0) && (!in_array($patternType,$this->styleGuideExcludes))) { $arrayReset = false; foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { // if there are no sub-items in a section remove it, else do a bunch of other stuff - if (!isset($patternSubtypeValues["patternSubtypeItems"])) { + if (count($patternSubtypeValues["patternSubtypeItems"]) == 0) { unset($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]); $arrayReset = true; @@ -902,14 +618,26 @@ protected function gatherPatternInfo() { // add to the view all paths $this->viewAllPaths[$patternTypeDash][$patternSubtypeDash] = $patternType."-".$patternSubtype; + // add the subtype info + $patternSubtypeData = array("patternName" => $patternSubtypeValues["patternSubtypeUC"], "patternSectionSubtype" => true); + if (isset($patternSubtypeValues["patternSubtypeDesc"])) { + $patternSubtypeData["patternDesc"] = $patternSubtypeValues["patternSubtypeDesc"]; + } + if (isset($patternSubtypeValues["patternSubtypeMeta"])) { + $patternSubtypeData["patternDesc"] = $patternSubtypeValues["patternSubtypeDesc"]; + } + $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = $patternSubtypeData; + // add patterns to $this->patternPartials foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItem) { - $patternCode = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); - $patternCodeRaw = $patternCode[0]; - $patternCodeEncoded = $patternCode[1]; - $patternLineageExists = (count($this->patternLineages[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; - $patternLineages = $this->patternLineages[$patternSubtypeItem["patternPartial"]]; + $patternCode = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); + $patternCodeRaw = $patternCode[0]; + $patternCodeEncoded = $patternCode[1]; + $patternLineageExists = (count($this->patternLineages[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; + $patternLineages = $this->patternLineages[$patternSubtypeItem["patternPartial"]]; + $patternLineageRExists = (count($this->patternLineagesR[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; + $patternLineagesR = $this->patternLineagesR[$patternSubtypeItem["patternPartial"]]; // set-up the mark-up for CSS Rule Saver so it can figure out which rules to save $patternCSSExists = $this->enableCSS; @@ -920,16 +648,29 @@ protected function gatherPatternInfo() { $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; } - $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = array("patternName" => $patternSubtypeItem["patternName"], - "patternLink" => $patternSubtypeItem["patternPath"], - "patternPartial" => $patternSubtypeItem["patternPartial"], - "patternPartialCode" => $patternCodeRaw, - "patternPartialCodeE" => $patternCodeEncoded, - "patternCSSExists" => $patternCSSExists, - "patternCSS" => $patternCSS, - "patternLineageExists" => $patternLineageExists, - "patternLineages" => $patternLineages - ); + $patternPartialData = array("patternSectionVanilla" => true, + "patternName" => $patternSubtypeItem["patternName"], + "patternLink" => $patternSubtypeItem["patternPath"], + "patternPartial" => $patternSubtypeItem["patternPartial"], + "patternPartialCode" => $patternCodeRaw, + "patternPartialCodeE" => $patternCodeEncoded, + "patternCSSExists" => $patternCSSExists, + "patternCSS" => $patternCSS, + "patternLineageExists" => $patternLineageExists, + "patternLineages" => $patternLineages, + "patternLineageRExists" => $patternLineageRExists, + "patternLineagesR" => $patternLineagesR + ); + + if (isset($patternSubtypeItem["patternDesc"])) { + $patternPartialData["patternDesc"] = $patternSubtypeItem["patternDesc"]; + } + + if (isset($patternSubtypeItem["patternMeta"])) { + $patternPartialData["patternMeta"] = $patternSubtypeItem["patternMeta"]; + } + + $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = $patternPartialData; // set the pattern state $patternBits = $this->getPatternInfo($patternSubtypeItem["patternPartial"]); @@ -1001,8 +742,8 @@ protected function getListItems($filepath) { $data = file_get_contents($filepath); $listItemsJSON = json_decode($data, true); - if ($jsonErrorMessage = $this->jsonHasError()) { - $this->jsonLastErrorMsg(str_replace($this->sd."/","",$filepath),$jsonErrorMessage,$data); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg(str_replace($this->sd."/","",$filepath),$jsonErrorMessage,$data); } $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); @@ -1306,39 +1047,6 @@ protected function initializeCSSRuleSaver() { } - /** - * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 - * @param {String} the file that generated the error - */ - protected function jsonHasError() { - $errors = array( - JSON_ERROR_NONE => false, - JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', - JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', - JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', - JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', - JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded' - ); - $error = json_last_error(); - $errorMessage = array_key_exists($error, $errors) ? $errors[$error] : "Unknown error ({$error})"; - return $errorMessage; - } - - /** - * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 - * @param {String} the file that generated the error - */ - protected function jsonLastErrorMsg($file,$message,$data) { - print "\nThe JSON file, ".$file.", wasn't loaded. The error: ".$message."\n"; - if ($message == "Syntax error, malformed JSON") { - print "\n"; - $parser = new \Seld\JsonLint\JsonParser(); - $error = $parser->lint($data); - print $error->getMessage(); - print "\n\n"; - } - } - /** * Print out the data var. For debugging purposes * From afcf3cd2afa55b3107cde700aee0325ac63bd98c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:50:02 -0400 Subject: [PATCH 076/166] should give the right file name --- core/lib/PatternLab/PatternLoaders/Mustache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/PatternLoaders/Mustache.php b/core/lib/PatternLab/PatternLoaders/Mustache.php index 416708f74..73dedb5b1 100644 --- a/core/lib/PatternLab/PatternLoaders/Mustache.php +++ b/core/lib/PatternLab/PatternLoaders/Mustache.php @@ -102,7 +102,7 @@ protected function loadFile($name) { // throw error if path is not found if (!file_exists($fileName)) { - throw new \Mustache_Exception_UnknownTemplateException($name); + throw new \Mustache_Exception_UnknownTemplateException($fileName); } // get the file data From b8caf6739098e8c39c4103f6c065521a3d15ad55 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 19 May 2014 16:50:24 -0400 Subject: [PATCH 077/166] adding support for markdown file tracking --- core/lib/PatternLab/Watcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index 491a8922b..e73d73da1 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -70,7 +70,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals $fileName = str_replace($this->sd."/_patterns".DIRECTORY_SEPARATOR,"",$name); $fileNameClean = str_replace(DIRECTORY_SEPARATOR."_",DIRECTORY_SEPARATOR,$fileName); - if ($object->isFile() && (($object->getExtension() == "mustache") || ($object->getExtension() == "json"))) { + if ($object->isFile() && (($object->getExtension() == "mustache") || ($object->getExtension() == "json") || ($object->getExtension() == "md"))) { // make sure this isn't a hidden pattern $patternParts = explode(DIRECTORY_SEPARATOR,$fileName); From b71b88be9c631a8ed9f0795546282fd16cf43357 Mon Sep 17 00:00:00 2001 From: Brad Frost Date: Tue, 20 May 2014 11:39:26 -0400 Subject: [PATCH 078/166] Fixed "excerpt" typo, related to issue #196 --- core/source/_patterns/04-pages/00-homepage.json | 10 +++++----- core/source/_patterns/04-pages/01-blog.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/source/_patterns/04-pages/00-homepage.json b/core/source/_patterns/04-pages/00-homepage.json index b42aa6061..186cf82ea 100644 --- a/core/source/_patterns/04-pages/00-homepage.json +++ b/core/source/_patterns/04-pages/00-homepage.json @@ -61,7 +61,7 @@ "headline" : { "short" : "Navigate the Allegheny River" }, - "exerpt" : { + "excerpt" : { "medium" : "The Allegheny River is a principal tributary of the Ohio River; it is located in the Eastern United States. The Allegheny River joins with the Monongahela River to form the Ohio River at the Point of Point State Park in Downtown Pittsburgh, Pennsylvania." } }, @@ -75,7 +75,7 @@ "headline" : { "short" : "How to detect and avoid poison ivy" }, - "exerpt" : { + "excerpt" : { "medium" : "Toxicodendron radicans, commonly known as poison ivy is a poisonous North American plant that is well known for its production of urushiol" } }, @@ -89,7 +89,7 @@ "headline" : { "short" : "Top 10 hiking mountains" }, - "exerpt" : { + "excerpt" : { "medium" : "Yosemite National Park is a United States National Park spanning eastern portions of Tuolumne, Mariposa and Madera counties in the central eastern portion of the U.S. state of California." } }, @@ -103,7 +103,7 @@ "headline" : { "short" : "How to build a campfire" }, - "exerpt" : { + "excerpt" : { "medium" : "A campfire is a fire lit at a campsite, to serve the following functions: light, warmth, a beacon, an insect and/or apex predator deterrent, to cook, and for a psychological sense of security. " } }, @@ -117,7 +117,7 @@ "headline" : { "short" : "Pick the right camping gear" }, - "exerpt" : { + "excerpt" : { "medium" : "The equipment used in camping varies with the particular type of camping. For instance, in survival camping the equipment consists of small items which have the purpose of helping the camper in providing food, heat and safety." } } diff --git a/core/source/_patterns/04-pages/01-blog.json b/core/source/_patterns/04-pages/01-blog.json index a4d03ba08..e0d8ed504 100644 --- a/core/source/_patterns/04-pages/01-blog.json +++ b/core/source/_patterns/04-pages/01-blog.json @@ -12,7 +12,7 @@ "headline" : { "short" : "Navigate the Allegheny River" }, - "exerpt" : { + "excerpt" : { "medium" : "The Allegheny River is a principal tributary of the Ohio River; it is located in the Eastern United States. The Allegheny River joins with the Monongahela River to form the Ohio River at the Point of Point State Park in Downtown Pittsburgh, Pennsylvania." } }, @@ -26,7 +26,7 @@ "headline" : { "short" : "How to detect and avoid poison ivy" }, - "exerpt" : { + "excerpt" : { "medium" : "Toxicodendron radicans, commonly known as poison ivy is a poisonous North American plant that is well known for its production of urushiol" } }, @@ -40,7 +40,7 @@ "headline" : { "short" : "Top 10 hiking mountains" }, - "exerpt" : { + "excerpt" : { "medium" : "Yosemite National Park is a United States National Park spanning eastern portions of Tuolumne, Mariposa and Madera counties in the central eastern portion of the U.S. state of California." } }, @@ -54,7 +54,7 @@ "headline" : { "short" : "How to build a campfire" }, - "exerpt" : { + "excerpt" : { "medium" : "A campfire is a fire lit at a campsite, to serve the following functions: light, warmth, a beacon, an insect and/or apex predator deterrent, to cook, and for a psychological sense of security. " } }, @@ -68,7 +68,7 @@ "headline" : { "short" : "Pick the right camping gear" }, - "exerpt" : { + "excerpt" : { "medium" : "The equipment used in camping varies with the particular type of camping. For instance, in survival camping the equipment consists of small items which have the purpose of helping the camper in providing food, heat and safety." } } From 582be7e5741d8b13040923428921b01fc915f309 Mon Sep 17 00:00:00 2001 From: Brad Frost Date: Tue, 20 May 2014 11:42:31 -0400 Subject: [PATCH 079/166] Make logo link to homepage to demonstrate the "link" functionality. --- core/source/_patterns/00-atoms/04-images/00-logo.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/source/_patterns/00-atoms/04-images/00-logo.mustache b/core/source/_patterns/00-atoms/04-images/00-logo.mustache index c0f431f98..8fa9d34c3 100644 --- a/core/source/_patterns/00-atoms/04-images/00-logo.mustache +++ b/core/source/_patterns/00-atoms/04-images/00-logo.mustache @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From b4f4094cb30be499a18061019094e185d3ee9283 Mon Sep 17 00:00:00 2001 From: Brad Frost Date: Tue, 20 May 2014 11:46:38 -0400 Subject: [PATCH 080/166] Update latest-post block and blog page to inherit the blog template --- .../03-sections/00-latest-posts.mustache | 16 +++++------- .../_patterns/04-pages/01-blog.mustache | 26 +------------------ 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache b/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache index 42f32dfa9..55b1cf401 100644 --- a/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache +++ b/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache @@ -1,11 +1,9 @@
    -

    Latest Posts

    -
      -
    • {{> molecules-media-block }}
    • -
    • {{> molecules-media-block }}
    • -
    • {{> molecules-media-block }}
    • -
    • {{> molecules-media-block }}
    • -
    • {{> molecules-media-block }}
    • -
    - View more posts +

    Latest Posts

    +
      + {{# latest-posts}} +
    • {{> molecules-media-block }}
    • + {{/ latest-posts}} +
    + View more posts
    \ No newline at end of file diff --git a/core/source/_patterns/04-pages/01-blog.mustache b/core/source/_patterns/04-pages/01-blog.mustache index 6d8e26c15..bd1218023 100644 --- a/core/source/_patterns/04-pages/01-blog.mustache +++ b/core/source/_patterns/04-pages/01-blog.mustache @@ -1,25 +1 @@ -
    - {{> organisms-header }} -
    -

    Our Outdoor Blog

    -
    -
    -
    - -
      - {{# latest-posts}} -
    • {{> molecules-media-block }}
    • - {{/ latest-posts}} -
    - View more posts -
    - {{> molecules-pagination }} -
    - - -
    -
    - {{> organisms-footer }} -
    \ No newline at end of file +{{> templates-blog }} \ No newline at end of file From 219e7d509c95592faf655fa81c3e3744e190ebd2 Mon Sep 17 00:00:00 2001 From: Brad Frost Date: Tue, 20 May 2014 11:51:21 -0400 Subject: [PATCH 081/166] Alert updated to use styleModifier --- .../_patterns/01-molecules/07-messaging/00-alert.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache b/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache index 50f390a85..971484885 100644 --- a/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache +++ b/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache @@ -1,3 +1,3 @@ -
    +
    {{ excerpt.short }}
    \ No newline at end of file From 01576e89e8bf8b4789d840c0164cd3b1baa4e4ca Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:11:36 -0400 Subject: [PATCH 082/166] adding Scan\KSS --- core/lib/scan/kss-php/.editorconfig | 13 + core/lib/scan/kss-php/.gitignore | 3 + core/lib/scan/kss-php/ACKNOWLEDGEMENTS | 23 + core/lib/scan/kss-php/CONTRIBUTING.md | 41 ++ core/lib/scan/kss-php/LICENSE | 20 + core/lib/scan/kss-php/README.md | 125 +++++ core/lib/scan/kss-php/build.xml | 92 ++++ core/lib/scan/kss-php/composer.json | 29 + core/lib/scan/kss-php/composer.lock | 475 ++++++++++++++++ .../kss-php/lib/Scan/Kss/CommentParser.php | 226 ++++++++ .../Scan/Kss/Exception/ExceptionInterface.php | 7 + .../Exception/UnexpectedValueException.php | 7 + .../scan/kss-php/lib/Scan/Kss/Modifier.php | 236 ++++++++ core/lib/scan/kss-php/lib/Scan/Kss/Parser.php | 196 +++++++ .../lib/scan/kss-php/lib/Scan/Kss/Section.php | 518 ++++++++++++++++++ core/lib/scan/kss-php/lib/Scan/kss.coffee | 41 ++ core/lib/scan/kss-php/phpunit.xml.dist | 28 + 17 files changed, 2080 insertions(+) create mode 100644 core/lib/scan/kss-php/.editorconfig create mode 100644 core/lib/scan/kss-php/.gitignore create mode 100644 core/lib/scan/kss-php/ACKNOWLEDGEMENTS create mode 100644 core/lib/scan/kss-php/CONTRIBUTING.md create mode 100644 core/lib/scan/kss-php/LICENSE create mode 100644 core/lib/scan/kss-php/README.md create mode 100644 core/lib/scan/kss-php/build.xml create mode 100644 core/lib/scan/kss-php/composer.json create mode 100644 core/lib/scan/kss-php/composer.lock create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Exception/UnexpectedValueException.php create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Modifier.php create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Parser.php create mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Section.php create mode 100644 core/lib/scan/kss-php/lib/Scan/kss.coffee create mode 100644 core/lib/scan/kss-php/phpunit.xml.dist diff --git a/core/lib/scan/kss-php/.editorconfig b/core/lib/scan/kss-php/.editorconfig new file mode 100644 index 000000000..97557e71d --- /dev/null +++ b/core/lib/scan/kss-php/.editorconfig @@ -0,0 +1,13 @@ +; http://EditorConfig.org + +root = true + +[*] +indent_style = space +indent_size = 4 + +charset = utf-8 +end_of_line = lf + +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/core/lib/scan/kss-php/.gitignore b/core/lib/scan/kss-php/.gitignore new file mode 100644 index 000000000..d9b450841 --- /dev/null +++ b/core/lib/scan/kss-php/.gitignore @@ -0,0 +1,3 @@ +vendor/ +build/ +phpunit.xml diff --git a/core/lib/scan/kss-php/ACKNOWLEDGEMENTS b/core/lib/scan/kss-php/ACKNOWLEDGEMENTS new file mode 100644 index 000000000..1e3a16d32 --- /dev/null +++ b/core/lib/scan/kss-php/ACKNOWLEDGEMENTS @@ -0,0 +1,23 @@ +Contents of this project are a php conversion of the original ruby version found +at http://github.com/kneath/kss which included the following license: + +Copyright (c) 2011 Tom Preston-Werner, Kyle Neath + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +Software), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/scan/kss-php/CONTRIBUTING.md b/core/lib/scan/kss-php/CONTRIBUTING.md new file mode 100644 index 000000000..e31443909 --- /dev/null +++ b/core/lib/scan/kss-php/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Code Modifications + +## Guidelines + +All code modifications must following [PSR-0][], [PSR-1][], and [PSR-2][] as +outlined on the [PHP Framework Interop Group][php-fig]. + +An .editorconfig file is included to help setup your environment if your IDE supports +[EditorConfig][]. + +## Procedure + +* Fork the repository and create a topic branch from where you want to base your work. + * This is usually the master branch. + * To quickly create a topic branch based on master; `git branch + my_contribution master` then checkout the new branch with `git + checkout my_contribution`. Please avoid working directly on the + `master` branch. +* Make commits of logical units. +* Check for unnecessary whitespace with `git diff --check` before committing. +* Make sure your commit messages are in the proper format. + * If your commit messages do not follow this format, please do a + `git rebase -i master` to reword your commit messages. + +```` + Subject Line Describing Your Changes + + The body of your commit message should describe the behavior without your + changes, why this is a problem, and how your changes fix the problem when + applied. +```` + +* Make sure you have added the necessary tests for your changes. +* Run all the tests to assure nothing else was accidentally broken. + +[PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md +[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md +[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md +[php-fig]: http://www.php-fig.org +[EditorConfig]: http://editorconfig.org/ + diff --git a/core/lib/scan/kss-php/LICENSE b/core/lib/scan/kss-php/LICENSE new file mode 100644 index 000000000..c588a7033 --- /dev/null +++ b/core/lib/scan/kss-php/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Scan, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +Software), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/scan/kss-php/README.md b/core/lib/scan/kss-php/README.md new file mode 100644 index 000000000..6be9f7dc3 --- /dev/null +++ b/core/lib/scan/kss-php/README.md @@ -0,0 +1,125 @@ +# Knyle Style Sheets + +This is a PHP implementation of [Knyle Style Sheets](http://warpspire.com/kss) (KSS). +KSS attempts to provide a methodology for writing maintainable, documented CSS +within a team. Specifically, KSS is a documentation specification and styleguide +format. It is **not** a preprocessor, CSS framework, naming convention, or +specificity guideline. + +* **[The Spec (What KSS is)](https://github.com/kneath/kss/blob/master/SPEC.md)** +* **[Example living styleguide](https://github.com/scaninc/kss-php/tree/master/example)** + +## KSS in a nutshell + +The methodology and ideas behind Knyle Style Sheets are contained in [SPEC.md](https://github.com/kneath/kss/blob/master/SPEC.md) +of the origin [ruby version](https://github.com/kneath/kss) of KSS. At its core, +KSS is a documenting syntax for CSS. + +```css +/* +# Star Button + +A button suitable for giving stars to someone. + +Markup: Button + +:hover - Subtle hover highlight. +.stars--given - A highlight indicating you've already given a star. +.stars--given:hover - Subtle hover highlight on top of stars-given styling. +.stars--disabled - Dims the button to indicate it cannot be used. + +Styleguide 2.1.3. +*/ +a.button.star { + ... +} +a.button.star:hover { + ... +} +a.button.stars--given { + ... +} +a.button.stars--given:hover { + ... +} +a.button.stars--disabled { + ... +} +``` + +## PHP Library + +This repository includes a php library suitable for parsing SASS, SCSS, and CSS +documented with KSS guidelines. To use the library, include it in your project as +a composer dependency (see below). Then, create a parser and explore your KSS. + +```php +getSection('2.1.1'); +// Returns a \Scan\Kss\Section object + +echo $section->getTitle(); +// Echoes "Star Button" + +echo $section->getDescription(); +// echoes "A button suitable for giving stars to someone." + +echo $section->getMarkup(); +// echoes "Button" + +$modifier = current($section->getModifiers()); +// Returns a \Scan\Kss\Modifier object + +echo $modifier->getName(); +// echoes ':hover' + +echo $modifier->getClassName(); +// echoes 'psuedo-class-hover' + +echo $modifier->getDescription(); +// echoes 'Subtle hover highlight' + +echo $modifier->getExampleHtml(); +// echoes Button for the .stars-given modifier +``` + +## Generating styleguides + +The documenting syntax and php library are intended to generate styleguides automatically. +To do this, you'll need to leverage a small javascript library that generates +class styles for pseudo-class styles (`:hover`, `:disabled`, etc). + +* [kss.coffee](https://github.com/scaninc/kss-php/blob/master/lib/Scan/kss.coffee) +* [kss.js](https://github.com/scaninc/kss-php/blob/master/example/public/js/kss.js) (compiled js) + +For an example of how to generate a styleguide, check out the [`example`](https://github.com/scaninc/kss-php/tree/master/example) +php pages. + +## Dependencies + +The PHP version of KSS has dependencies managed by Composer. If you did not install +kss-php using composer, you must install these dependencies manually before using +the library by running the following commands: + +``` +$ composer install +``` + +If you do not yet have Composer, download it following the instructions on +http://getcomposer.org or run the following commands to install it globally on +your system: + +``` +$ curl -s https://getcomposer.org/installer | php +$ sudo mv composer.phar /usr/local/bin/composer +``` + +## Symfony2 Bundle + +If your project uses [symfony2](http://symfony.com/), consider using the [KSS Bundle] +(https://github.com/scaninc/ScanKssBundle) as well. The KSS Bundle uses Twig templates +to make the styleguide block easier to customize and include in your views. diff --git a/core/lib/scan/kss-php/build.xml b/core/lib/scan/kss-php/build.xml new file mode 100644 index 000000000..e59f6fde2 --- /dev/null +++ b/core/lib/scan/kss-php/build.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/lib/scan/kss-php/composer.json b/core/lib/scan/kss-php/composer.json new file mode 100644 index 000000000..912ec67e6 --- /dev/null +++ b/core/lib/scan/kss-php/composer.json @@ -0,0 +1,29 @@ +{ + "name": "scan/kss-php", + "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", + "keywords": [ + "kss", + "styleguide", + "css documentation" + ], + "license": "MIT", + "require": { + "php": ">=5.3.3", + "symfony/finder": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "authors": [ + { + "name": "Russell Ahlstrom", + "email": "russell.ahlstrom@gmail.com", + "homepage": "http://russell.ahlstromology.com" + } + ], + "autoload": { + "psr-0": { + "Scan\\Kss": "lib/" + } + } +} diff --git a/core/lib/scan/kss-php/composer.lock b/core/lib/scan/kss-php/composer.lock new file mode 100644 index 000000000..047fdb498 --- /dev/null +++ b/core/lib/scan/kss-php/composer.lock @@ -0,0 +1,475 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "52293773fb81531edc12e7533e95963a", + "packages": [ + { + "name": "symfony/finder", + "version": "v2.3.0", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "v2.3.0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.3.0", + "reference": "v2.3.0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2013-06-02 12:05:51" + } + ], + "packages-dev": [ + { + "name": "phpunit/php-code-coverage", + "version": "1.2.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "1.2.11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.11", + "reference": "1.2.11", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": ">=1.3.0@stable", + "phpunit/php-text-template": ">=1.1.1@stable", + "phpunit/php-token-stream": ">=1.1.3@stable" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.0.5" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2013-05-23 18:23:24" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.3", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "1.3.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", + "reference": "1.3.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2012-10-11 04:44:38" + }, + { + "name": "phpunit/php-text-template", + "version": "1.1.4", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-text-template.git", + "reference": "1.1.4" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4", + "reference": "1.1.4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2012-10-31 11:15:28" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.4", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-timer.git", + "reference": "1.0.4" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", + "reference": "1.0.4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "timer" + ], + "time": "2012-10-11 04:45:58" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.1.5", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/php-token-stream.git", + "reference": "1.1.5" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", + "reference": "1.1.5", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "tokenizer" + ], + "time": "2012-10-11 04:47:14" + }, + { + "name": "phpunit/phpunit", + "version": "3.7.21", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "3.7.21" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.21", + "reference": "3.7.21", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", + "phpunit/php-file-iterator": ">=1.3.1", + "phpunit/php-text-template": ">=1.1.1", + "phpunit/php-timer": ">=1.0.2,<1.1.0", + "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", + "symfony/yaml": ">=2.0,<3.0" + }, + "require-dev": { + "pear-pear/pear": "1.9.4" + }, + "suggest": { + "ext-json": "*", + "ext-simplexml": "*", + "ext-tokenizer": "*", + "phpunit/php-invoker": ">=1.1.0,<1.2.0" + }, + "bin": [ + "composer/bin/phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2013-05-23 18:54:29" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "1.2.3", + "source": { + "type": "git", + "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "1.2.3" + }, + "dist": { + "type": "zip", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", + "reference": "1.2.3", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-text-template": ">=1.1.1@stable" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHPUnit/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2013-01-13 10:24:48" + }, + { + "name": "symfony/yaml", + "version": "v2.2.2", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "v2.2.2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.2.2", + "reference": "v2.2.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2013-05-10 18:08:31" + } + ], + "aliases": [ + + ], + "minimum-stability": "stable", + "stability-flags": [ + + ], + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [ + + ] +} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php b/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php new file mode 100644 index 000000000..0f8297fc4 --- /dev/null +++ b/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php @@ -0,0 +1,226 @@ +file = $file; + $this->options = $options; + } + + /** + * Returns the parsed comment blocks or if object is not yet parsed, parses + * first and then returns the result + * + * @return array + */ + public function getBlocks() + { + if (!$this->parsed) { + $this->parseBlocks(); + } + return $this->blocks; + } + + /** + * Parses each line of the file looking for single or multi-line comments + * + * @return array + */ + protected function parseBlocks() + { + $this->blocks = array(); + $currentBlock = ''; + // Do we need insideSingleLineBlock? It doesn't seem to be used anywhere + // Original Ruby version of KSS had it but I'm not seeing a purpose to it + $insideSingleLineBlock = false; + $insideMultiLineBlock = false; + + foreach ($this->file as $line) { + $isSingleLineComment = self::isSingleLineComment($line); + $isStartMultiLineComment = self::isStartMultiLineComment($line); + $isEndMultiLineComment = self::isEndMultiLineComment($line); + + if ($isSingleLineComment) { + $parsed = self::parseSingleLineComment($line); + + if ($insideSingleLineBlock) { + $currentBlock .= "\n"; + } else { + $insideSingleLineBlock = true; + } + + $currentBlock .= $parsed; + } + + if ($isStartMultiLineComment || $insideMultiLineBlock) { + $parsed = self::parseMultiLineComment($line); + + if ($insideMultiLineBlock) { + $currentBlock .= "\n"; + } else { + $insideMultiLineBlock = true; + } + + $currentBlock .= $parsed; + } + + if ($isEndMultiLineComment) { + $insideMultiLineBlock = false; + } + + // If we're not in a comment then end the current block and go to + // the next one + if (!$isSingleLineComment && !$insideMultiLineBlock) { + if (!empty($currentBlock)) { + $this->blocks[] = $this->normalize($currentBlock); + $insideSingleLineBlock = false; + $currentBlock = ''; + } + } + } + + $this->parsed = true; + return $this->blocks; + } + + /** + * Makes all the white space consistent among the lines in a comment block. + * That is if the first and second line had 10 spaces but the third line was + * indented to 15 spaces, we'd normalize it so the first and second line have + * no spaces and the third line has 5 spaces. + * + * @param string $block + * + * @return string + */ + protected function normalize($block) + { + // Remove any [whitespace]*'s from the start of each line + $normalizedBlock = preg_replace('-^\s*\*+-m', '', $block); + + $indentSize = null; + $blockLines = explode("\n", $normalizedBlock); + $normalizedLines = array(); + foreach ($blockLines as $line) { + preg_match('/^\s*/', $line, $matches); + $precedingWhitespace = strlen($matches[0]); + if ($indentSize === null) { + $indentSize = $precedingWhitespace; + } + + if ($indentSize <= $precedingWhitespace && $indentSize > 0) { + $line = substr($line, $indentSize); + } + + $normalizedLines[] = $line; + } + + return trim(implode("\n", $normalizedLines)); + } + + /** + * Checks if the comment is a single line comment + * + * @param string $line + * + * @return boolean + */ + public static function isSingleLineComment($line) + { + return (bool) preg_match('-^\s*//-', $line); + } + + /** + * Checks if the line is the start of a multi-line comment + * + * @param string $line + * + * @return boolean + */ + public static function isStartMultiLineComment($line) + { + return (bool) preg_match('-^\s*/\*-', $line); + } + + /** + * Checks if the line is the end of a multi-line comment + * + * @param string $line + * + * @return boolean + */ + public static function isEndMultiLineComment($line) + { + return (bool) preg_match('-.*\*/-', $line); + } + + /** + * Removes the comment markers from a single line comment and trims the line + * + * @param string $line + * + * @return string + */ + public static function parseSingleLineComment($line) + { + return rtrim(preg_replace('-^\s*//-', '', $line)); + } + + /** + * Removes the comment markers from a multi line comment and trims the line + * + * @param string $line + * + * @return string + */ + public static function parseMultiLineComment($line) + { + $parsed = preg_replace('-^\s*/\*+-', '', $line); + $parsed = preg_replace('-\*/-', '', $parsed); + return rtrim($parsed); + } +} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php b/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php new file mode 100644 index 000000000..c6237450d --- /dev/null +++ b/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php @@ -0,0 +1,7 @@ +setName($name); + $this->setDescription($description); + } + + /** + * Returns the name of the modifier + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the name of the modifier + * + * @param string $name + */ + public function setName($name) + { + $name = $this->parseExtend($name); + $this->name = $name; + } + + /** + * Returns the description of the modifier + * + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the description of the modifier + * + * @param string $description + */ + public function setDescription($description) + { + $this->description = $description; + } + + /** + * Returns the markup of the modifier + * + * @return string + */ + public function getMarkup() + { + return $this->markup; + } + + /** + * Sets the markup of the modifier + * + * @param string $markup + */ + public function setMarkup($markup) + { + $this->markup = $markup; + } + + /** + * Checks the name for any extend notations and parses that information + * off and stores it in the $this->extendedClass + * + * @param string $name + * + * @return $name + */ + protected function parseExtend($name) + { + $this->setExtendedClass(null); + + $nameParts = explode('@extend', $name); + $name = trim($nameParts[0]); + if (count($nameParts) > 1) { + $this->setExtendedClass($nameParts[1]); + } + + return $name; + } + + /** + * Returns whether the modifier is applied by extension + * + * @return boolean + */ + public function isExtender() + { + return (bool) $this->getExtendedClass(); + } + + /** + * Returns the extended class + * + * @return string + */ + public function getExtendedClass() + { + return $this->extendedClass; + } + + /** + * Sets the extended class. If the class name is empty, assuming null instead + * and stop further parsing + * + * @param string $class + */ + public function setExtendedClass($class) + { + if (empty($class)) { + $this->extenderClass = null; + return; + } + + $this->extendedClass = trim($class); + } + + /** + * Returns the class name for the extended class + * + * @return string + */ + public function getExtendedClassName() + { + if ($this->getExtendedClass() === null) { + return ''; + } + + $name = str_replace('%', ' ', $this->getExtendedClass()); + $name = str_replace('.', ' ', $name); + $name = str_replace(':', ' pseudo-class-', $name); + return trim($name); + } + + /** + * Returns the class name for the modifier + * + * @return string + */ + public function getClassName() + { + $name = str_replace('.', ' ', $this->name); + $name = str_replace(':', ' pseudo-class-', $name); + return trim($name); + } + + /** + * Returns a string of specified html with inserted class names in the correct + * places for modifiers and extenders. + * + * @param string $html OPTIONAL + * + * @return string $html + */ + public function getExampleHtml($html = null) + { + if ($html === null) { + if ($this->getMarkup() === null) { + return ''; + } + $html = $this->getMarkup(); + } + + if ($this->isExtender()) { + $html = str_replace('$modifierClass', '', $html); + + // Use a positive lookbehind and lookahead to ensure we don't + // replace anything more than just the targeted class name + // for example an element name that is the same as the extended + // class name (e.g. button) + $pattern = sprintf('/(?<="| )%s(?="| )/', $this->getExtendedClassName()); + $html = preg_replace( + $pattern, + $this->getClassName(), + $html + ); + } + + $html = str_replace('$modifierClass', $this->getClassName(), $html); + + return $html; + } +} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php b/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php new file mode 100644 index 000000000..0204ec2af --- /dev/null +++ b/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php @@ -0,0 +1,196 @@ +files()->name('/\.(css|sass|scss|less)$/')->in($paths); + + foreach ($finder as $fileInfo) { + $file = new \splFileObject($fileInfo); + $commentParser = new CommentParser($file); + foreach ($commentParser->getBlocks() as $commentBlock) { + if (self::isKssBlock($commentBlock)) { + $this->addSection($commentBlock, $file); + } + } + } + } + + /** + * Adds a section to the Sections collection + * + * @param string $comment + * @param \splFileObject $file + */ + protected function addSection($comment, \splFileObject $file) + { + $section = new Section($comment, $file); + $this->sections[$section->getReference(true)] = $section; + $this->sectionsSortedByReference = false; + } + + /** + * Returns a Section object matching the requested reference. If reference + * is not found, an empty Section object is returned instead + * + * @param string $reference + * + * @return Section + * + * @throws UnexepectedValueException if reference does not exist + */ + public function getSection($reference) + { + $reference = Section::trimReference($reference); + if (array_key_exists($reference, $this->sections)) { + return $this->sections[$reference]; + } + return false; + } + + /** + * Returns an array of all the sections + * + * @return array + */ + public function getSections() + { + $this->sortSections(); + return $this->sections; + } + + /** + * Returns only the top level sections (i.e. 1.0, 2.0, 3.0, etc.) + * + * @return array + */ + public function getTopLevelSections() + { + $this->sortSectionsByDepth(); + $topLevelSections = array(); + + foreach ($this->sections as $section) { + if ($section->getDepth() != 0) { + break; + } + $topLevelSections[] = $section; + } + + return $topLevelSections; + } + + /** + * Returns an array of children for a specified section reference + * + * @param string $reference + * @param int $levelsDown OPTIONAL + * + * @return array + */ + public function getSectionChildren($reference, $levelsDown = null) + { + $this->sortSections(); + + $sectionKeys = array_keys($this->sections); + $sections = array(); + + $maxDepth = null; + if ($levelsDown !== null) { + $maxDepth = Section::calcDepth($reference) + $levelsDown; + } + + $reference = Section::trimReference($reference); + $reference .= '.'; + + foreach ($sectionKeys as $sectionKey) { + // Only get sections within that level. Do not get the level itself + if (strpos($sectionKey . '.', $reference) === 0 + && $sectionKey . '.' != $reference + ) { + $section = $this->sections[$sectionKey]; + if ($maxDepth !== null && $section->getDepth() > $maxDepth) { + continue; + } + $sections[$sectionKey] = $section; + } + } + + return $sections; + } + + /** + * Method to only sort the sections if they need sorting + * + * @return void + */ + protected function sortSections() + { + if ($this->sectionsSortedByReference) { + return; + } + + uasort($this->sections, '\Scan\Kss\Section::depthScoreSort'); + $this->sectionsSortedByReference = true; + } + + /** + * Method to sort the sections by depth + * + * @return void + */ + protected function sortSectionsByDepth() + { + uasort($this->sections, '\Scan\Kss\Section::depthSort'); + $this->sectionsSortedByReference = false; + } + + /** + * Checks to see if a comment block is a KSS Comment block + * + * @param string $comment + * + * @return boolean + */ + public static function isKssBlock($comment) + { + $commentLines = explode("\n\n", $comment); + $lastLine = end($commentLines); + return (bool) preg_match('/Pattern \S/i', $lastLine); + } +} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Section.php b/core/lib/scan/kss-php/lib/Scan/Kss/Section.php new file mode 100644 index 000000000..7679f9a09 --- /dev/null +++ b/core/lib/scan/kss-php/lib/Scan/Kss/Section.php @@ -0,0 +1,518 @@ +rawComment = $comment; + $this->file = $file; + } + + /** + * Returns the source filename for where the comment block was located + * + * @return string + */ + public function getFilename() + { + if ($this->file === null) { + return ''; + } + + return $this->file->getFilename(); + } + + /** + * Returns the title of the section + * + * @return string + */ + public function getTitle() + { + $title = ''; + + $titleComment = $this->getTitleComment(); + if (preg_match('/^\s*#+\s*(.+)/', $titleComment, $matches)) { + $title = $matches[1]; + } + + return $title; + } + + /** + * Returns the description for the section + * + * @return string + */ + public function getDescription() + { + $descriptionSections = array(); + + foreach ($this->getCommentSections() as $commentSection) { + // Anything that is not the section comment or modifiers comment + // must be the description comment + if ($commentSection != $this->getReferenceComment() + && $commentSection != $this->getTitleComment() + && $commentSection != $this->getMarkupComment() + && $commentSection != $this->getDeprecatedComment() + && $commentSection != $this->getExperimentalComment() + && $commentSection != $this->getModifiersComment() + ) { + $descriptionSections[] = $commentSection; + } + } + + return implode("\n\n", $descriptionSections); + } + + /** + * Returns the markup defined in the section + * + * @return string + */ + public function getMarkup() + { + if ($this->markup === null) { + if ($markupComment = $this->getMarkupComment()) { + $this->markup = trim(preg_replace('/^\s*Markup:/i', '', $markupComment)); + } + } + + return $this->markup; + } + + /** + * Returns the markup for the normal element (without modifierclass) + * + * @param string $replacement Replacement for $modifierClass variable + * @return void + */ + public function getMarkupNormal($replacement = '') + { + return str_replace('$modifierClass', $replacement, $this->getMarkup()); + } + + /** + * Returns the deprecation notice defined in the section + * + * @return string + */ + public function getDeprecated() + { + if ($this->deprecated === null) { + if ($deprecatedComment = $this->getDeprecatedComment()) { + $this->deprecated = trim(preg_replace('/^\s*Deprecated:/i', '', $deprecatedComment)); + } + } + + return $this->deprecated; + } + + /** + * Returns the experimental notice defined in the section + * + * @return string + */ + public function getExperimental() + { + if ($this->experimental === null) { + if ($experimentalComment = $this->getExperimentalComment()) { + $this->experimental = trim(preg_replace('/^\s*Experimental:/i', '', $experimentalComment)); + } + } + + return $this->experimental; + } + + /** + * Returns the modifiers used in the section + * + * @return array + */ + public function getModifiers() + { + $lastIndent = null; + $modifiers = array(); + + if ($modiferComment = $this->getModifiersComment()) { + $modifierLines = explode("\n", $modiferComment); + foreach ($modifierLines as $line) { + if (empty($line)) { + continue; + } + + preg_match('/^\s*/', $line, $matches); + $indent = strlen($matches[0]); + + if ($lastIndent && $indent > $lastIndent) { + $modifier = end($modifiers); + $modifier->setDescription($modifier->getDescription() + trim($line)); + } else { + $lineParts = explode(' - ', $line); + + $name = trim(array_shift($lineParts)); + + $description = ''; + if (!empty($lineParts)) { + $description = trim(implode(' - ', $lineParts)); + } + $modifier = new Modifier($name, $description); + + // If the CSS has a markup, pass it to the modifier for the example HTML + if ($markup = $this->getMarkup()) { + $modifier->setMarkup($markup); + } + $modifiers[] = $modifier; + } + } + } + + return $modifiers; + } + + /** + * Returns the reference number for the section + * + * @return string + * + * @deprecated Method deprecated in v0.3.1 + */ + public function getSection() + { + return $this->getReference(); + } + + /** + * Returns the reference number for the section + * + * @param boolean $trimmed OPTIONAL + * + * @return string + */ + public function getReference($trimmed = false) + { + if ($this->reference === null) { + $referenceComment = $this->getReferenceComment(); + $referenceComment = preg_replace('/\.$/', '', $referenceComment); + + if (preg_match('/Pattern (\S*)/', $referenceComment, $matches)) { + $this->reference = $matches[1]; + } + } + + return ($trimmed && $this->reference !== null) + ? self::trimReference($this->reference) + : $this->reference; + } + + /** + * Trims off all trailing zeros and periods on a reference + * + * @param string $reference + * + * @return string + */ + public static function trimReference($reference) + { + if (substr($reference, -1) == '.') { + $reference = substr($reference, 0, -1); + } + while (preg_match('/(\.0+)$/', $reference, $matches)) { + $reference = substr($reference, 0, strlen($matches[1]) * -1); + } + return $reference; + } + + /** + * Checks to see if a section belongs to a specified reference + * + * @param string $reference + * + * @return boolean + */ + public function belongsToReference($reference) + { + $reference = self::trimReference($reference); + return strpos($this->getReference() . '.', $reference . '.') === 0; + } + + /** + * Helper method for calculating the depth of the instantiated section + * + * @return int + */ + public function getDepth() + { + return self::calcDepth($this->getReference()); + } + + /** + * Calculates and returns the depth of a section reference + * + * @param string $reference + * + * @return int + */ + public static function calcDepth($reference) + { + $reference = self::trimReference($reference); + return substr_count($reference, '.'); + } + + /** + * Helper method for calculating the score of the instantiated section + * + * @return int + */ + public function getDepthScore() + { + return self::calcDepthScore($this->getReference()); + } + /** + * Calculates and returns the depth score for the section. Useful for sorting + * sections correctly by their section reference numbers + * + * @return int + */ + public static function calcDepthScore($reference) + { + $reference = self::trimReference($reference); + $sectionParts = explode('.', $reference); + $score = 0; + foreach ($sectionParts as $level => $part) { + $score += $part * (1 / pow(10, $level)); + } + return $score; + } + + /** + * Function to help sort sections by depth and then depth score + * + * @param Section $a + * @param Section $b + * + * @return int + */ + public static function depthSort(Section $a, Section $b) + { + if ($a->getDepth() == $b->getDepth()) { + return self::depthScoreSort($a, $b); + } + return $a->getDepth() > $b->getDepth(); + } + + /** + * Function to help sort sections by their depth score + * + * @param Section $a + * @param Section $b + * + * @return int + */ + public static function depthScoreSort(Section $a, Section $b) + { + return $a->getDepthScore() > $b->getDepthScore(); + } + + /** + * Returns the comment block used when creating the section as an array of + * paragraphs within the comment block + * + * @return array + */ + protected function getCommentSections() + { + if (empty($this->commentSections) && $this->rawComment) { + $this->commentSections = explode("\n\n", $this->rawComment); + } + + return $this->commentSections; + } + + /** + * Gets the title part of the KSS Comment Block + * + * @return string + */ + protected function getTitleComment() + { + $titleComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Identify the title by the # markdown header syntax + if (preg_match('/^\s*#/i', $commentSection)) { + $titleComment = $commentSection; + break; + } + } + + return $titleComment; + } + + /** + * Returns the part of the KSS Comment Block that contains the markup + * + * @return string + */ + protected function getMarkupComment() + { + $markupComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Identify the markup comment by the Markup: marker + if (preg_match('/^\s*Markup:/i', $commentSection)) { + $markupComment = $commentSection; + break; + } + } + + return $markupComment; + } + + /** + * Returns the part of the KSS Comment Block that contains the deprecated + * notice + * + * @return string + */ + protected function getDeprecatedComment() + { + $deprecatedComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Identify the deprecation notice by the Deprecated: marker + if (preg_match('/^\s*Deprecated:/i', $commentSection)) { + $deprecatedComment = $commentSection; + break; + } + } + + return $deprecatedComment; + } + + /** + * Returns the part of the KSS Comment Block that contains the experimental + * notice + * + * @return string + */ + protected function getExperimentalComment() + { + $experimentalComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Identify the experimental notice by the Experimental: marker + if (preg_match('/^\s*Experimental:/i', $commentSection)) { + $experimentalComment = $commentSection; + break; + } + } + + return $experimentalComment; + } + + /** + * Gets the part of the KSS Comment Block that contains the section reference + * + * @return string + */ + protected function getReferenceComment() + { + $referenceComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Identify it by the Styleguide 1.2.3. pattern + if (preg_match('/Pattern \S/i', $commentSection)) { + $referenceComment = $commentSection; + break; + } + } + + return $referenceComment; + } + + /** + * Returns the part of the KSS Comment Block that contains the modifiers + * + * @return string + */ + protected function getModifiersComment() + { + $modifiersComment = null; + + foreach ($this->getCommentSections() as $commentSection) { + // Assume that the modifiers section starts with either a class or a + // pseudo class + if (preg_match('/^\s*(?:\.|:)/', $commentSection)) { + $modifiersComment = $commentSection; + break; + } + } + + return $modifiersComment; + } +} diff --git a/core/lib/scan/kss-php/lib/Scan/kss.coffee b/core/lib/scan/kss-php/lib/Scan/kss.coffee new file mode 100644 index 000000000..62d66e441 --- /dev/null +++ b/core/lib/scan/kss-php/lib/Scan/kss.coffee @@ -0,0 +1,41 @@ +# This class scans your stylesheets for pseudo classes, then inserts a new CSS +# rule with the same properties, but named 'psuedo-class-{{name}}'. +# +# Supported pseudo classes: hover, disabled, active, visited, focus. +# +# Example: +# +# a:hover{ color:blue; } +# => a.pseudo-class-hover{ color:blue; } +class KssStateGenerator + constructor: -> + pseudos = /(\:hover|\:disabled|\:active|\:visited|\:focus|\:target)/g + + try + for stylesheet in document.styleSheets + if stylesheet.href.indexOf(document.domain) >= 0 + idxs = [] + for rule, idx in stylesheet.cssRules + if (rule.type == CSSRule.STYLE_RULE) && pseudos.test(rule.selectorText) + replaceRule = (matched, stuff) -> + return matched.replace(/\:/g, '.pseudo-class-') + @insertRule(rule.cssText.replace(pseudos, replaceRule)) + pseudos.lastIndex = 0 + + # Takes a given style and attaches it to the current page. + # + # rule - A CSS rule String (ex: ".test{ display:none; }"). + # + # Returns nothing. + insertRule: (rule) -> + headEl = document.getElementsByTagName('head')[0] + styleEl = document.createElement('style') + styleEl.type = 'text/css' + if styleEl.styleSheet + styleEl.styleSheet.cssText = rule + else + styleEl.appendChild(document.createTextNode(rule)) + + headEl.appendChild(styleEl) + +new KssStateGenerator diff --git a/core/lib/scan/kss-php/phpunit.xml.dist b/core/lib/scan/kss-php/phpunit.xml.dist new file mode 100644 index 000000000..ed1ba195e --- /dev/null +++ b/core/lib/scan/kss-php/phpunit.xml.dist @@ -0,0 +1,28 @@ + + + + + + + test + + + + + + + + + + + + lib + + + From 7d71925aca50f92e9dba88ca7e0654ce23b44c5b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:12:58 -0400 Subject: [PATCH 083/166] adding symfony/finder --- .../Symfony/Component/Finder/.gitignore | 3 + .../Finder/Adapter/AbstractAdapter.php | 236 +++++ .../Finder/Adapter/AbstractFindAdapter.php | 327 +++++++ .../Finder/Adapter/AdapterInterface.php | 144 +++ .../Finder/Adapter/BsdFindAdapter.php | 103 +++ .../Finder/Adapter/GnuFindAdapter.php | 104 +++ .../Component/Finder/Adapter/PhpAdapter.php | 98 +++ .../Symfony/Component/Finder/CHANGELOG.md | 30 + .../Finder/Comparator/Comparator.php | 98 +++ .../Finder/Comparator/DateComparator.php | 54 ++ .../Finder/Comparator/NumberComparator.php | 82 ++ .../Exception/AccessDeniedException.php | 19 + .../Exception/AdapterFailureException.php | 46 + .../Finder/Exception/ExceptionInterface.php | 23 + .../OperationNotPermitedException.php | 19 + .../ShellCommandFailureException.php | 45 + .../Finder/Expression/Expression.php | 146 +++ .../Component/Finder/Expression/Glob.php | 157 ++++ .../Component/Finder/Expression/Regex.php | 321 +++++++ .../Finder/Expression/ValueInterface.php | 60 ++ .../Symfony/Component/Finder/Finder.php | 829 ++++++++++++++++++ .../finder/Symfony/Component/Finder/Glob.php | 103 +++ .../Finder/Iterator/CustomFilterIterator.php | 63 ++ .../Iterator/DateRangeFilterIterator.php | 60 ++ .../Iterator/DepthRangeFilterIterator.php | 47 + .../ExcludeDirectoryFilterIterator.php | 55 ++ .../Finder/Iterator/FilePathsIterator.php | 131 +++ .../Iterator/FileTypeFilterIterator.php | 55 ++ .../Iterator/FilecontentFilterIterator.php | 76 ++ .../Iterator/FilenameFilterIterator.php | 68 ++ .../Finder/Iterator/FilterIterator.php | 49 ++ .../Iterator/MultiplePcreFilterIterator.php | 66 ++ .../Finder/Iterator/PathFilterIterator.php | 75 ++ .../Iterator/RecursiveDirectoryIterator.php | 126 +++ .../Iterator/SizeRangeFilterIterator.php | 59 ++ .../Finder/Iterator/SortableIterator.php | 82 ++ .../finder/Symfony/Component/Finder/LICENSE | 19 + .../finder/Symfony/Component/Finder/README.md | 41 + .../Component/Finder/Shell/Command.php | 294 +++++++ .../Symfony/Component/Finder/Shell/Shell.php | 95 ++ .../Symfony/Component/Finder/SplFileInfo.php | 77 ++ .../Symfony/Component/Finder/composer.json | 31 + .../Symfony/Component/Finder/phpunit.xml.dist | 29 + 43 files changed, 4645 insertions(+) create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/.gitignore create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Finder.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Glob.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/LICENSE create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/README.md create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/composer.json create mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore b/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore new file mode 100644 index 000000000..c49a5d8df --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php new file mode 100644 index 000000000..bdb04f535 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php @@ -0,0 +1,236 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +/** + * Interface for finder engine implementations. + * + * @author Jean-François Simon + */ +abstract class AbstractAdapter implements AdapterInterface +{ + protected $followLinks = false; + protected $mode = 0; + protected $minDepth = 0; + protected $maxDepth = PHP_INT_MAX; + protected $exclude = array(); + protected $names = array(); + protected $notNames = array(); + protected $contains = array(); + protected $notContains = array(); + protected $sizes = array(); + protected $dates = array(); + protected $filters = array(); + protected $sort = false; + protected $paths = array(); + protected $notPaths = array(); + protected $ignoreUnreadableDirs = false; + + private static $areSupported = array(); + + /** + * {@inheritdoc} + */ + public function isSupported() + { + $name = $this->getName(); + + if (!array_key_exists($name, self::$areSupported)) { + self::$areSupported[$name] = $this->canBeUsed(); + } + + return self::$areSupported[$name]; + } + + /** + * {@inheritdoc} + */ + public function setFollowLinks($followLinks) + { + $this->followLinks = $followLinks; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setMode($mode) + { + $this->mode = $mode; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDepths(array $depths) + { + $this->minDepth = 0; + $this->maxDepth = PHP_INT_MAX; + + foreach ($depths as $comparator) { + switch ($comparator->getOperator()) { + case '>': + $this->minDepth = $comparator->getTarget() + 1; + break; + case '>=': + $this->minDepth = $comparator->getTarget(); + break; + case '<': + $this->maxDepth = $comparator->getTarget() - 1; + break; + case '<=': + $this->maxDepth = $comparator->getTarget(); + break; + default: + $this->minDepth = $this->maxDepth = $comparator->getTarget(); + } + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setExclude(array $exclude) + { + $this->exclude = $exclude; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNames(array $names) + { + $this->names = $names; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotNames(array $notNames) + { + $this->notNames = $notNames; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setContains(array $contains) + { + $this->contains = $contains; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotContains(array $notContains) + { + $this->notContains = $notContains; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSizes(array $sizes) + { + $this->sizes = $sizes; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setDates(array $dates) + { + $this->dates = $dates; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setFilters(array $filters) + { + $this->filters = $filters; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setSort($sort) + { + $this->sort = $sort; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPath(array $paths) + { + $this->paths = $paths; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setNotPath(array $notPaths) + { + $this->notPaths = $notPaths; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function ignoreUnreadableDirs($ignore = true) + { + $this->ignoreUnreadableDirs = (bool) $ignore; + + return $this; + } + + /** + * Returns whether the adapter is supported in the current environment. + * + * This method should be implemented in all adapters. Do not implement + * isSupported in the adapters as the generic implementation provides a cache + * layer. + * + * @see isSupported + * + * @return bool Whether the adapter is supported + */ + abstract protected function canBeUsed(); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php new file mode 100644 index 000000000..01940385d --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php @@ -0,0 +1,327 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +use Symfony\Component\Finder\Exception\AccessDeniedException; +use Symfony\Component\Finder\Iterator; +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Expression\Expression; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Comparator\NumberComparator; +use Symfony\Component\Finder\Comparator\DateComparator; + +/** + * Shell engine implementation using GNU find command. + * + * @author Jean-François Simon + */ +abstract class AbstractFindAdapter extends AbstractAdapter +{ + /** + * @var Shell + */ + protected $shell; + + /** + * Constructor. + */ + public function __construct() + { + $this->shell = new Shell(); + } + + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + // having "/../" in path make find fail + $dir = realpath($dir); + + // searching directories containing or not containing strings leads to no result + if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) { + return new Iterator\FilePathsIterator(array(), $dir); + } + + $command = Command::create(); + $find = $this->buildFindCommand($command, $dir); + + if ($this->followLinks) { + $find->add('-follow'); + } + + $find->add('-mindepth')->add($this->minDepth + 1); + + if (PHP_INT_MAX !== $this->maxDepth) { + $find->add('-maxdepth')->add($this->maxDepth + 1); + } + + if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) { + $find->add('-type d'); + } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) { + $find->add('-type f'); + } + + $this->buildNamesFiltering($find, $this->names); + $this->buildNamesFiltering($find, $this->notNames, true); + $this->buildPathsFiltering($find, $dir, $this->paths); + $this->buildPathsFiltering($find, $dir, $this->notPaths, true); + $this->buildSizesFiltering($find, $this->sizes); + $this->buildDatesFiltering($find, $this->dates); + + $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs'); + $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut'); + + if ($useGrep && ($this->contains || $this->notContains)) { + $grep = $command->ins('grep'); + $this->buildContentFiltering($grep, $this->contains); + $this->buildContentFiltering($grep, $this->notContains, true); + } + + if ($useSort) { + $this->buildSorting($command, $this->sort); + } + + $command->setErrorHandler( + $this->ignoreUnreadableDirs + // If directory is unreadable and finder is set to ignore it, `stderr` is ignored. + ? function ($stderr) { return; } + : function ($stderr) { throw new AccessDeniedException($stderr); } + ); + + $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute()); + $iterator = new Iterator\FilePathsIterator($paths, $dir); + + if ($this->exclude) { + $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); + } + + if (!$useGrep && ($this->contains || $this->notContains)) { + $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); + } + + if ($this->filters) { + $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); + } + + if (!$useSort && $this->sort) { + $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); + $iterator = $iteratorAggregate->getIterator(); + } + + return $iterator; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return $this->shell->testCommand('find'); + } + + /** + * @param Command $command + * @param string $dir + * + * @return Command + */ + protected function buildFindCommand(Command $command, $dir) + { + return $command + ->ins('find') + ->add('find ') + ->arg($dir) + ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions + } + + /** + * @param Command $command + * @param string[] $names + * @param bool $not + */ + private function buildNamesFiltering(Command $command, array $names, $not = false) + { + if (0 === count($names)) { + return; + } + + $command->add($not ? '-not' : null)->cmd('('); + + foreach ($names as $i => $name) { + $expr = Expression::create($name); + + // Find does not support expandable globs ("*.{a,b}" syntax). + if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { + $expr = Expression::create($expr->getGlob()->toRegex(false)); + } + + // Fixes 'not search' and 'full path matching' regex problems. + // - Jokers '.' are replaced by [^/]. + // - We add '[^/]*' before and after regex (if no ^|$ flags are present). + if ($expr->isRegex()) { + $regex = $expr->getRegex(); + $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*') + ->setStartFlag(false) + ->setStartJoker(true) + ->replaceJokers('[^/]'); + if (!$regex->hasEndFlag() || $regex->hasEndJoker()) { + $regex->setEndJoker(false)->append('[^/]*'); + } + } + + $command + ->add($i > 0 ? '-or' : null) + ->add($expr->isRegex() + ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') + : ($expr->isCaseSensitive() ? '-name' : '-iname') + ) + ->arg($expr->renderPattern()); + } + + $command->cmd(')'); + } + + /** + * @param Command $command + * @param string $dir + * @param string[] $paths + * @param bool $not + */ + private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false) + { + if (0 === count($paths)) { + return; + } + + $command->add($not ? '-not' : null)->cmd('('); + + foreach ($paths as $i => $path) { + $expr = Expression::create($path); + + // Find does not support expandable globs ("*.{a,b}" syntax). + if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { + $expr = Expression::create($expr->getGlob()->toRegex(false)); + } + + // Fixes 'not search' regex problems. + if ($expr->isRegex()) { + $regex = $expr->getRegex(); + $regex->prepend($regex->hasStartFlag() ? $dir.DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag()); + } else { + $expr->prepend('*')->append('*'); + } + + $command + ->add($i > 0 ? '-or' : null) + ->add($expr->isRegex() + ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') + : ($expr->isCaseSensitive() ? '-path' : '-ipath') + ) + ->arg($expr->renderPattern()); + } + + $command->cmd(')'); + } + + /** + * @param Command $command + * @param NumberComparator[] $sizes + */ + private function buildSizesFiltering(Command $command, array $sizes) + { + foreach ($sizes as $i => $size) { + $command->add($i > 0 ? '-and' : null); + + switch ($size->getOperator()) { + case '<=': + $command->add('-size -'.($size->getTarget() + 1).'c'); + break; + case '>=': + $command->add('-size +'. ($size->getTarget() - 1).'c'); + break; + case '>': + $command->add('-size +'.$size->getTarget().'c'); + break; + case '!=': + $command->add('-size -'.$size->getTarget().'c'); + $command->add('-size +'.$size->getTarget().'c'); + case '<': + default: + $command->add('-size -'.$size->getTarget().'c'); + } + } + } + + /** + * @param Command $command + * @param DateComparator[] $dates + */ + private function buildDatesFiltering(Command $command, array $dates) + { + foreach ($dates as $i => $date) { + $command->add($i > 0 ? '-and' : null); + + $mins = (int) round((time()-$date->getTarget()) / 60); + + if (0 > $mins) { + // mtime is in the future + $command->add(' -mmin -0'); + // we will have no result so we don't need to continue + return; + } + + switch ($date->getOperator()) { + case '<=': + $command->add('-mmin +'.($mins - 1)); + break; + case '>=': + $command->add('-mmin -'.($mins + 1)); + break; + case '>': + $command->add('-mmin -'.$mins); + break; + case '!=': + $command->add('-mmin +'.$mins.' -or -mmin -'.$mins); + break; + case '<': + default: + $command->add('-mmin +'.$mins); + } + } + } + + /** + * @param Command $command + * @param string $sort + * + * @throws \InvalidArgumentException + */ + private function buildSorting(Command $command, $sort) + { + $this->buildFormatSorting($command, $sort); + } + + /** + * @param Command $command + * @param string $sort + */ + abstract protected function buildFormatSorting(Command $command, $sort); + + /** + * @param Command $command + * @param array $contains + * @param bool $not + */ + abstract protected function buildContentFiltering(Command $command, array $contains, $not = false); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php new file mode 100644 index 000000000..e25fc3e1f --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +/** + * @author Jean-François Simon + */ +interface AdapterInterface +{ + /** + * @param bool $followLinks + * + * @return AdapterInterface Current instance + */ + public function setFollowLinks($followLinks); + + /** + * @param int $mode + * + * @return AdapterInterface Current instance + */ + public function setMode($mode); + + /** + * @param array $exclude + * + * @return AdapterInterface Current instance + */ + public function setExclude(array $exclude); + + /** + * @param array $depths + * + * @return AdapterInterface Current instance + */ + public function setDepths(array $depths); + + /** + * @param array $names + * + * @return AdapterInterface Current instance + */ + public function setNames(array $names); + + /** + * @param array $notNames + * + * @return AdapterInterface Current instance + */ + public function setNotNames(array $notNames); + + /** + * @param array $contains + * + * @return AdapterInterface Current instance + */ + public function setContains(array $contains); + + /** + * @param array $notContains + * + * @return AdapterInterface Current instance + */ + public function setNotContains(array $notContains); + + /** + * @param array $sizes + * + * @return AdapterInterface Current instance + */ + public function setSizes(array $sizes); + + /** + * @param array $dates + * + * @return AdapterInterface Current instance + */ + public function setDates(array $dates); + + /** + * @param array $filters + * + * @return AdapterInterface Current instance + */ + public function setFilters(array $filters); + + /** + * @param \Closure|int $sort + * + * @return AdapterInterface Current instance + */ + public function setSort($sort); + + /** + * @param array $paths + * + * @return AdapterInterface Current instance + */ + public function setPath(array $paths); + + /** + * @param array $notPaths + * + * @return AdapterInterface Current instance + */ + public function setNotPath(array $notPaths); + + /** + * @param bool $ignore + * + * @return AdapterInterface Current instance + */ + public function ignoreUnreadableDirs($ignore = true); + + /** + * @param string $dir + * + * @return \Iterator Result iterator + */ + public function searchInDirectory($dir); + + /** + * Tests adapter support for current platform. + * + * @return bool + */ + public function isSupported(); + + /** + * Returns adapter name. + * + * @return string + */ + public function getName(); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php new file mode 100644 index 000000000..4a25baeb6 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Expression\Expression; + +/** + * Shell engine implementation using BSD find command. + * + * @author Jean-François Simon + */ +class BsdFindAdapter extends AbstractFindAdapter +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'bsd_find'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed(); + } + + /** + * {@inheritdoc} + */ + protected function buildFormatSorting(Command $command, $sort) + { + switch ($sort) { + case SortableIterator::SORT_BY_NAME: + $command->ins('sort')->add('| sort'); + + return; + case SortableIterator::SORT_BY_TYPE: + $format = '%HT'; + break; + case SortableIterator::SORT_BY_ACCESSED_TIME: + $format = '%a'; + break; + case SortableIterator::SORT_BY_CHANGED_TIME: + $format = '%c'; + break; + case SortableIterator::SORT_BY_MODIFIED_TIME: + $format = '%m'; + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); + } + + $command + ->add('-print0 | xargs -0 stat -f') + ->arg($format.'%t%N') + ->add('| sort | cut -f 2'); + } + + /** + * {@inheritdoc} + */ + protected function buildFindCommand(Command $command, $dir) + { + parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1); + + return $command; + } + + /** + * {@inheritdoc} + */ + protected function buildContentFiltering(Command $command, array $contains, $not = false) + { + foreach ($contains as $contain) { + $expr = Expression::create($contain); + + // todo: avoid forking process for each $pattern by using multiple -e options + $command + ->add('| grep -v \'^$\'') + ->add('| xargs -I{} grep -I') + ->add($expr->isCaseSensitive() ? null : '-i') + ->add($not ? '-L' : '-l') + ->add('-Ee')->arg($expr->renderPattern()) + ->add('{}') + ; + } + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php new file mode 100644 index 000000000..b72451ee1 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +use Symfony\Component\Finder\Shell\Shell; +use Symfony\Component\Finder\Shell\Command; +use Symfony\Component\Finder\Iterator\SortableIterator; +use Symfony\Component\Finder\Expression\Expression; + +/** + * Shell engine implementation using GNU find command. + * + * @author Jean-François Simon + */ +class GnuFindAdapter extends AbstractFindAdapter +{ + /** + * {@inheritdoc} + */ + public function getName() + { + return 'gnu_find'; + } + + /** + * {@inheritdoc} + */ + protected function buildFormatSorting(Command $command, $sort) + { + switch ($sort) { + case SortableIterator::SORT_BY_NAME: + $command->ins('sort')->add('| sort'); + + return; + case SortableIterator::SORT_BY_TYPE: + $format = '%y'; + break; + case SortableIterator::SORT_BY_ACCESSED_TIME: + $format = '%A@'; + break; + case SortableIterator::SORT_BY_CHANGED_TIME: + $format = '%C@'; + break; + case SortableIterator::SORT_BY_MODIFIED_TIME: + $format = '%T@'; + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); + } + + $command + ->get('find') + ->add('-printf') + ->arg($format.' %h/%f\\n') + ->add('| sort | cut') + ->arg('-d ') + ->arg('-f2-') + ; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed(); + } + + /** + * {@inheritdoc} + */ + protected function buildFindCommand(Command $command, $dir) + { + return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended'); + } + + /** + * {@inheritdoc} + */ + protected function buildContentFiltering(Command $command, array $contains, $not = false) + { + foreach ($contains as $contain) { + $expr = Expression::create($contain); + + // todo: avoid forking process for each $pattern by using multiple -e options + $command + ->add('| xargs -I{} -r grep -I') + ->add($expr->isCaseSensitive() ? null : '-i') + ->add($not ? '-L' : '-l') + ->add('-Ee')->arg($expr->renderPattern()) + ->add('{}') + ; + } + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php new file mode 100644 index 000000000..378a26acd --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Adapter; + +use Symfony\Component\Finder\Iterator; + +/** + * PHP finder engine implementation. + * + * @author Jean-François Simon + */ +class PhpAdapter extends AbstractAdapter +{ + /** + * {@inheritdoc} + */ + public function searchInDirectory($dir) + { + $flags = \RecursiveDirectoryIterator::SKIP_DOTS; + + if ($this->followLinks) { + $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + } + + $iterator = new \RecursiveIteratorIterator( + new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs), + \RecursiveIteratorIterator::SELF_FIRST + ); + + if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) { + $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth); + } + + if ($this->mode) { + $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); + } + + if ($this->exclude) { + $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); + } + + if ($this->names || $this->notNames) { + $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); + } + + if ($this->contains || $this->notContains) { + $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); + } + + if ($this->sizes) { + $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); + } + + if ($this->dates) { + $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); + } + + if ($this->filters) { + $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); + } + + if ($this->sort) { + $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); + $iterator = $iteratorAggregate->getIterator(); + } + + if ($this->paths || $this->notPaths) { + $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); + } + + return $iterator; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'php'; + } + + /** + * {@inheritdoc} + */ + protected function canBeUsed() + { + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md b/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md new file mode 100644 index 000000000..7ad230813 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md @@ -0,0 +1,30 @@ +CHANGELOG +========= + +2.3.0 +----- + + * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs()) + * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception + +2.2.0 +----- + + * added Finder::path() and Finder::notPath() methods + * added finder adapters to improve performance on specific platforms + * added support for wildcard characters (glob patterns) in the paths passed + to Finder::in() + +2.1.0 +----- + + * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and + Finder::sortByModifiedTime() + * added Countable to Finder + * added support for an array of directories as an argument to + Finder::exclude() + * added searching based on the file content via Finder::contains() and + Finder::notContains() + * added support for the != operator in the Comparator + * [BC BREAK] filter expressions (used for file name and content) are no more + considered as regexps but glob patterns when they are enclosed in '*' or '?' diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php new file mode 100644 index 000000000..4f5e1ffe1 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Comparator; + +/** + * Comparator. + * + * @author Fabien Potencier + */ +class Comparator +{ + private $target; + private $operator = '=='; + + /** + * Gets the target value. + * + * @return string The target value + */ + public function getTarget() + { + return $this->target; + } + + /** + * Sets the target value. + * + * @param string $target The target value + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Gets the comparison operator. + * + * @return string The operator + */ + public function getOperator() + { + return $this->operator; + } + + /** + * Sets the comparison operator. + * + * @param string $operator A valid operator + * + * @throws \InvalidArgumentException + */ + public function setOperator($operator) + { + if (!$operator) { + $operator = '=='; + } + + if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) { + throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); + } + + $this->operator = $operator; + } + + /** + * Tests against the target. + * + * @param mixed $test A test value + * + * @return bool + */ + public function test($test) + { + switch ($this->operator) { + case '>': + return $test > $this->target; + case '>=': + return $test >= $this->target; + case '<': + return $test < $this->target; + case '<=': + return $test <= $this->target; + case '!=': + return $test != $this->target; + } + + return $test == $this->target; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php new file mode 100644 index 000000000..9de444f05 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Comparator; + +/** + * DateCompare compiles date comparisons. + * + * @author Fabien Potencier + */ +class DateComparator extends Comparator +{ + + /** + * Constructor. + * + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct($test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); + } + + try { + $date = new \DateTime($matches[2]); + $target = $date->format('U'); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); + } + + $operator = isset($matches[1]) ? $matches[1] : '=='; + if ('since' === $operator || 'after' === $operator) { + $operator = '>'; + } + + if ('until' === $operator || 'before' === $operator) { + $operator = '<'; + } + + $this->setOperator($operator); + $this->setTarget($target); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php new file mode 100644 index 000000000..955cc6755 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Comparator; + +/** + * NumberComparator compiles a simple comparison to an anonymous + * subroutine, which you can call with a value to be tested again. + * + * Now this would be very pointless, if NumberCompare didn't understand + * magnitudes. + * + * The target value may use magnitudes of kilobytes (k, ki), + * megabytes (m, mi), or gigabytes (g, gi). Those suffixed + * with an i use the appropriate 2**n version in accordance with the + * IEC standard: http://physics.nist.gov/cuu/Units/binary.html + * + * Based on the Perl Number::Compare module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + * + * @see http://physics.nist.gov/cuu/Units/binary.html + */ +class NumberComparator extends Comparator +{ + /** + * Constructor. + * + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct($test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); + } + + $target = $matches[2]; + if (!is_numeric($target)) { + throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); + } + if (isset($matches[3])) { + // magnitude + switch (strtolower($matches[3])) { + case 'k': + $target *= 1000; + break; + case 'ki': + $target *= 1024; + break; + case 'm': + $target *= 1000000; + break; + case 'mi': + $target *= 1024*1024; + break; + case 'g': + $target *= 1000000000; + break; + case 'gi': + $target *= 1024*1024*1024; + break; + } + } + + $this->setTarget($target); + $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php new file mode 100644 index 000000000..ee195ea8d --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Exception; + +/** + * @author Jean-François Simon + */ +class AccessDeniedException extends \UnexpectedValueException +{ +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php new file mode 100644 index 000000000..15fa22147 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Exception; + +use Symfony\Component\Finder\Adapter\AdapterInterface; + +/** + * Base exception for all adapter failures. + * + * @author Jean-François Simon + */ +class AdapterFailureException extends \RuntimeException implements ExceptionInterface +{ + /** + * @var \Symfony\Component\Finder\Adapter\AdapterInterface + */ + private $adapter; + + /** + * @param AdapterInterface $adapter + * @param string|null $message + * @param \Exception|null $previous + */ + public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null) + { + $this->adapter = $adapter; + parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous); + } + + /** + * {@inheritdoc} + */ + public function getAdapter() + { + return $this->adapter; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php new file mode 100644 index 000000000..bff02142d --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Exception; + +/** + * @author Jean-François Simon + */ +interface ExceptionInterface +{ + /** + * @return \Symfony\Component\Finder\Adapter\AdapterInterface + */ + public function getAdapter(); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php new file mode 100644 index 000000000..366311225 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Exception; + +/** + * @author Jean-François Simon + */ +class OperationNotPermitedException extends AdapterFailureException +{ +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php new file mode 100644 index 000000000..2658f6a50 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Exception; + +use Symfony\Component\Finder\Adapter\AdapterInterface; +use Symfony\Component\Finder\Shell\Command; + +/** + * @author Jean-François Simon + */ +class ShellCommandFailureException extends AdapterFailureException +{ + /** + * @var Command + */ + private $command; + + /** + * @param AdapterInterface $adapter + * @param Command $command + * @param \Exception|null $previous + */ + public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null) + { + $this->command = $command; + parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous); + } + + /** + * @return Command + */ + public function getCommand() + { + return $this->command; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php new file mode 100644 index 000000000..8559b33e3 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Expression implements ValueInterface +{ + const TYPE_REGEX = 1; + const TYPE_GLOB = 2; + + /** + * @var ValueInterface + */ + private $value; + + /** + * @param string $expr + * + * @return Expression + */ + public static function create($expr) + { + return new self($expr); + } + + /** + * @param string $expr + */ + public function __construct($expr) + { + try { + $this->value = Regex::create($expr); + } catch (\InvalidArgumentException $e) { + $this->value = new Glob($expr); + } + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * {@inheritdoc} + */ + public function render() + { + return $this->value->render(); + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return $this->value->renderPattern(); + } + + /** + * @return bool + */ + public function isCaseSensitive() + { + return $this->value->isCaseSensitive(); + } + + /** + * @return int + */ + public function getType() + { + return $this->value->getType(); + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->value->prepend($expr); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->value->append($expr); + + return $this; + } + + /** + * @return bool + */ + public function isRegex() + { + return self::TYPE_REGEX === $this->value->getType(); + } + + /** + * @return bool + */ + public function isGlob() + { + return self::TYPE_GLOB === $this->value->getType(); + } + + /** + * @throws \LogicException + * + * @return Glob + */ + public function getGlob() + { + if (self::TYPE_GLOB !== $this->value->getType()) { + throw new \LogicException('Regex can\'t be transformed to glob.'); + } + + return $this->value; + } + + /** + * @return Regex + */ + public function getRegex() + { + return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex(); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php new file mode 100644 index 000000000..3023ceea6 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Glob implements ValueInterface +{ + /** + * @var string + */ + private $pattern; + + /** + * @param string $pattern + */ + public function __construct($pattern) + { + $this->pattern = $pattern; + } + + /** + * {@inheritdoc} + */ + public function render() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return Expression::TYPE_GLOB; + } + + /** + * {@inheritdoc} + */ + public function isCaseSensitive() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->pattern = $expr.$this->pattern; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->pattern .= $expr; + + return $this; + } + + /** + * Tests if glob is expandable ("*.{a,b}" syntax). + * + * @return bool + */ + public function isExpandable() + { + return false !== strpos($this->pattern, '{') + && false !== strpos($this->pattern, '}'); + } + + /** + * @param bool $strictLeadingDot + * @param bool $strictWildcardSlash + * + * @return Regex + */ + public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true) + { + $firstByte = true; + $escaping = false; + $inCurlies = 0; + $regex = ''; + $sizeGlob = strlen($this->pattern); + for ($i = 0; $i < $sizeGlob; $i++) { + $car = $this->pattern[$i]; + if ($firstByte) { + if ($strictLeadingDot && '.' !== $car) { + $regex .= '(?=[^\.])'; + } + + $firstByte = false; + } + + if ('/' === $car) { + $firstByte = true; + } + + if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { + $regex .= "\\$car"; + } elseif ('*' === $car) { + $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); + } elseif ('?' === $car) { + $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); + } elseif ('{' === $car) { + $regex .= $escaping ? '\\{' : '('; + if (!$escaping) { + ++$inCurlies; + } + } elseif ('}' === $car && $inCurlies) { + $regex .= $escaping ? '}' : ')'; + if (!$escaping) { + --$inCurlies; + } + } elseif (',' === $car && $inCurlies) { + $regex .= $escaping ? ',' : '|'; + } elseif ('\\' === $car) { + if ($escaping) { + $regex .= '\\\\'; + $escaping = false; + } else { + $escaping = true; + } + + continue; + } else { + $regex .= $car; + } + $escaping = false; + } + + return new Regex('^'.$regex.'$'); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php new file mode 100644 index 000000000..2e8f507b4 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php @@ -0,0 +1,321 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Expression; + +/** + * @author Jean-François Simon + */ +class Regex implements ValueInterface +{ + const START_FLAG = '^'; + const END_FLAG = '$'; + const BOUNDARY = '~'; + const JOKER = '.*'; + const ESCAPING = '\\'; + + /** + * @var string + */ + private $pattern; + + /** + * @var array + */ + private $options; + + /** + * @var bool + */ + private $startFlag; + + /** + * @var bool + */ + private $endFlag; + + /** + * @var bool + */ + private $startJoker; + + /** + * @var bool + */ + private $endJoker; + + /** + * @param string $expr + * + * @return Regex + * + * @throws \InvalidArgumentException + */ + public static function create($expr) + { + if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) { + $start = substr($m[1], 0, 1); + $end = substr($m[1], -1); + + if ( + ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) + || ($start === '{' && $end === '}') + || ($start === '(' && $end === ')') + ) { + return new self(substr($m[1], 1, -1), $m[2], $end); + } + } + + throw new \InvalidArgumentException('Given expression is not a regex.'); + } + + /** + * @param string $pattern + * @param string $options + * @param string $delimiter + */ + public function __construct($pattern, $options = '', $delimiter = null) + { + if (null !== $delimiter) { + // removes delimiter escaping + $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern); + } + + $this->parsePattern($pattern); + $this->options = $options; + } + + /** + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * {@inheritdoc} + */ + public function render() + { + return self::BOUNDARY + .$this->renderPattern() + .self::BOUNDARY + .$this->options; + } + + /** + * {@inheritdoc} + */ + public function renderPattern() + { + return ($this->startFlag ? self::START_FLAG : '') + .($this->startJoker ? self::JOKER : '') + .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern) + .($this->endJoker ? self::JOKER : '') + .($this->endFlag ? self::END_FLAG : ''); + } + + /** + * {@inheritdoc} + */ + public function isCaseSensitive() + { + return !$this->hasOption('i'); + } + + /** + * {@inheritdoc} + */ + public function getType() + { + return Expression::TYPE_REGEX; + } + + /** + * {@inheritdoc} + */ + public function prepend($expr) + { + $this->pattern = $expr.$this->pattern; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append($expr) + { + $this->pattern .= $expr; + + return $this; + } + + /** + * @param string $option + * + * @return bool + */ + public function hasOption($option) + { + return false !== strpos($this->options, $option); + } + + /** + * @param string $option + * + * @return Regex + */ + public function addOption($option) + { + if (!$this->hasOption($option)) { + $this->options.= $option; + } + + return $this; + } + + /** + * @param string $option + * + * @return Regex + */ + public function removeOption($option) + { + $this->options = str_replace($option, '', $this->options); + + return $this; + } + + /** + * @param bool $startFlag + * + * @return Regex + */ + public function setStartFlag($startFlag) + { + $this->startFlag = $startFlag; + + return $this; + } + + /** + * @return bool + */ + public function hasStartFlag() + { + return $this->startFlag; + } + + /** + * @param bool $endFlag + * + * @return Regex + */ + public function setEndFlag($endFlag) + { + $this->endFlag = (bool) $endFlag; + + return $this; + } + + /** + * @return bool + */ + public function hasEndFlag() + { + return $this->endFlag; + } + + /** + * @param bool $startJoker + * + * @return Regex + */ + public function setStartJoker($startJoker) + { + $this->startJoker = $startJoker; + + return $this; + } + + /** + * @return bool + */ + public function hasStartJoker() + { + return $this->startJoker; + } + + /** + * @param bool $endJoker + * + * @return Regex + */ + public function setEndJoker($endJoker) + { + $this->endJoker = (bool) $endJoker; + + return $this; + } + + /** + * @return bool + */ + public function hasEndJoker() + { + return $this->endJoker; + } + + /** + * @param array $replacement + * + * @return Regex + */ + public function replaceJokers($replacement) + { + $replace = function ($subject) use ($replacement) { + $subject = $subject[0]; + $replace = 0 === substr_count($subject, '\\') % 2; + + return $replace ? str_replace('.', $replacement, $subject) : $subject; + }; + + $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern); + + return $this; + } + + /** + * @param string $pattern + */ + private function parsePattern($pattern) + { + if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) { + $pattern = substr($pattern, 1); + } + + if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) { + $pattern = substr($pattern, 2); + } + + if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) { + $pattern = substr($pattern, 0, -1); + } + + if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) { + $pattern = substr($pattern, 0, -2); + } + + $this->pattern = $pattern; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php new file mode 100644 index 000000000..34ce0e7ce --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Expression; + +/** + * @author Jean-François Simon + */ +interface ValueInterface +{ + /** + * Renders string representation of expression. + * + * @return string + */ + public function render(); + + /** + * Renders string representation of pattern. + * + * @return string + */ + public function renderPattern(); + + /** + * Returns value case sensitivity. + * + * @return bool + */ + public function isCaseSensitive(); + + /** + * Returns expression type. + * + * @return int + */ + public function getType(); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function prepend($expr); + + /** + * @param string $expr + * + * @return ValueInterface + */ + public function append($expr); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php b/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php new file mode 100644 index 000000000..e945fab13 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php @@ -0,0 +1,829 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +use Symfony\Component\Finder\Adapter\AdapterInterface; +use Symfony\Component\Finder\Adapter\GnuFindAdapter; +use Symfony\Component\Finder\Adapter\BsdFindAdapter; +use Symfony\Component\Finder\Adapter\PhpAdapter; +use Symfony\Component\Finder\Exception\ExceptionInterface; + +/** + * Finder allows to build rules to find files and directories. + * + * It is a thin wrapper around several specialized iterator classes. + * + * All rules may be invoked several times. + * + * All methods return the current Finder object to allow easy chaining: + * + * $finder = Finder::create()->files()->name('*.php')->in(__DIR__); + * + * @author Fabien Potencier + * + * @api + */ +class Finder implements \IteratorAggregate, \Countable +{ + const IGNORE_VCS_FILES = 1; + const IGNORE_DOT_FILES = 2; + + private $mode = 0; + private $names = array(); + private $notNames = array(); + private $exclude = array(); + private $filters = array(); + private $depths = array(); + private $sizes = array(); + private $followLinks = false; + private $sort = false; + private $ignore = 0; + private $dirs = array(); + private $dates = array(); + private $iterators = array(); + private $contains = array(); + private $notContains = array(); + private $adapters = array(); + private $paths = array(); + private $notPaths = array(); + private $ignoreUnreadableDirs = false; + + private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'); + + /** + * Constructor. + */ + public function __construct() + { + $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; + + $this + ->addAdapter(new GnuFindAdapter()) + ->addAdapter(new BsdFindAdapter()) + ->addAdapter(new PhpAdapter(), -50) + ->setAdapter('php') + ; + } + + /** + * Creates a new Finder. + * + * @return Finder A new Finder instance + * + * @api + */ + public static function create() + { + return new static(); + } + + /** + * Registers a finder engine implementation. + * + * @param AdapterInterface $adapter An adapter instance + * @param int $priority Highest is selected first + * + * @return Finder The current Finder instance + */ + public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 0) + { + $this->adapters[$adapter->getName()] = array( + 'adapter' => $adapter, + 'priority' => $priority, + 'selected' => false, + ); + + return $this->sortAdapters(); + } + + /** + * Sets the selected adapter to the best one according to the current platform the code is run on. + * + * @return Finder The current Finder instance + */ + public function useBestAdapter() + { + $this->resetAdapterSelection(); + + return $this->sortAdapters(); + } + + /** + * Selects the adapter to use. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return Finder The current Finder instance + */ + public function setAdapter($name) + { + if (!isset($this->adapters[$name])) { + throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name)); + } + + $this->resetAdapterSelection(); + $this->adapters[$name]['selected'] = true; + + return $this->sortAdapters(); + } + + /** + * Removes all adapters registered in the finder. + * + * @return Finder The current Finder instance + */ + public function removeAdapters() + { + $this->adapters = array(); + + return $this; + } + + /** + * Returns registered adapters ordered by priority without extra information. + * + * @return AdapterInterface[] + */ + public function getAdapters() + { + return array_values(array_map(function (array $adapter) { + return $adapter['adapter']; + }, $this->adapters)); + } + + /** + * Restricts the matching to directories only. + * + * @return Finder The current Finder instance + * + * @api + */ + public function directories() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; + + return $this; + } + + /** + * Restricts the matching to files only. + * + * @return Finder The current Finder instance + * + * @api + */ + public function files() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; + + return $this; + } + + /** + * Adds tests for the directory depth. + * + * Usage: + * + * $finder->depth('> 1') // the Finder will start matching at level 1. + * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point. + * + * @param int $level The depth level expression + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\DepthRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\NumberComparator + * + * @api + */ + public function depth($level) + { + $this->depths[] = new Comparator\NumberComparator($level); + + return $this; + } + + /** + * Adds tests for file dates (last modified). + * + * The date must be something that strtotime() is able to parse: + * + * $finder->date('since yesterday'); + * $finder->date('until 2 days ago'); + * $finder->date('> now - 2 hours'); + * $finder->date('>= 2005-10-15'); + * + * @param string $date A date rage string + * + * @return Finder The current Finder instance + * + * @see strtotime + * @see Symfony\Component\Finder\Iterator\DateRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\DateComparator + * + * @api + */ + public function date($date) + { + $this->dates[] = new Comparator\DateComparator($date); + + return $this; + } + + /** + * Adds rules that files must match. + * + * You can use patterns (delimited with / sign), globs or simple strings. + * + * $finder->name('*.php') + * $finder->name('/\.php$/') // same as above + * $finder->name('test.php') + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + * + * @api + */ + public function name($pattern) + { + $this->names[] = $pattern; + + return $this; + } + + /** + * Adds rules that files must not match. + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + * + * @api + */ + public function notName($pattern) + { + $this->notNames[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must match. + * + * Strings or PCRE patterns can be used: + * + * $finder->contains('Lorem ipsum') + * $finder->contains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator + */ + public function contains($pattern) + { + $this->contains[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must not match. + * + * Strings or PCRE patterns can be used: + * + * $finder->notContains('Lorem ipsum') + * $finder->notContains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator + */ + public function notContains($pattern) + { + $this->notContains[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->path('some/special/dir') + * $finder->path('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + */ + public function path($pattern) + { + $this->paths[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must not match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->notPath('some/special/dir') + * $finder->notPath('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator + */ + public function notPath($pattern) + { + $this->notPaths[] = $pattern; + + return $this; + } + + /** + * Adds tests for file sizes. + * + * $finder->size('> 10K'); + * $finder->size('<= 1Ki'); + * $finder->size(4); + * + * @param string $size A size range string + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SizeRangeFilterIterator + * @see Symfony\Component\Finder\Comparator\NumberComparator + * + * @api + */ + public function size($size) + { + $this->sizes[] = new Comparator\NumberComparator($size); + + return $this; + } + + /** + * Excludes directories. + * + * @param string|array $dirs A directory path or an array of directories + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function exclude($dirs) + { + $this->exclude = array_merge($this->exclude, (array) $dirs); + + return $this; + } + + /** + * Excludes "hidden" directories and files (starting with a dot). + * + * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function ignoreDotFiles($ignoreDotFiles) + { + if ($ignoreDotFiles) { + $this->ignore = $this->ignore | static::IGNORE_DOT_FILES; + } else { + $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES; + } + + return $this; + } + + /** + * Forces the finder to ignore version control directories. + * + * @param bool $ignoreVCS Whether to exclude VCS files or not + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator + * + * @api + */ + public function ignoreVCS($ignoreVCS) + { + if ($ignoreVCS) { + $this->ignore = $this->ignore | static::IGNORE_VCS_FILES; + } else { + $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES; + } + + return $this; + } + + /** + * Adds VCS patterns. + * + * @see ignoreVCS + * + * @param string|string[] $pattern VCS patterns to ignore + */ + public static function addVCSPattern($pattern) + { + foreach ((array) $pattern as $p) { + self::$vcsPatterns[] = $p; + } + + self::$vcsPatterns = array_unique(self::$vcsPatterns); + } + + /** + * Sorts files and directories by an anonymous function. + * + * The anonymous function receives two \SplFileInfo instances to compare. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @param \Closure $closure An anonymous function + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sort(\Closure $closure) + { + $this->sort = $closure; + + return $this; + } + + /** + * Sorts files and directories by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByName() + { + $this->sort = Iterator\SortableIterator::SORT_BY_NAME; + + return $this; + } + + /** + * Sorts files and directories by type (directories before files), then by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByType() + { + $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; + + return $this; + } + + /** + * Sorts files and directories by the last accessed time. + * + * This is the time that the file was last accessed, read or written to. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByAccessedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last inode changed time. + * + * This is the time that the inode information was last modified (permissions, owner, group or other metadata). + * + * On Windows, since inode is not available, changed time is actually the file creation time. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByChangedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last modified time. + * + * This is the last time the actual contents of the file were last modified. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\SortableIterator + * + * @api + */ + public function sortByModifiedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; + + return $this; + } + + /** + * Filters the iterator with an anonymous function. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @param \Closure $closure An anonymous function + * + * @return Finder The current Finder instance + * + * @see Symfony\Component\Finder\Iterator\CustomFilterIterator + * + * @api + */ + public function filter(\Closure $closure) + { + $this->filters[] = $closure; + + return $this; + } + + /** + * Forces the following of symlinks. + * + * @return Finder The current Finder instance + * + * @api + */ + public function followLinks() + { + $this->followLinks = true; + + return $this; + } + + /** + * Tells finder to ignore unreadable directories. + * + * By default, scanning unreadable directories content throws an AccessDeniedException. + * + * @param bool $ignore + * + * @return Finder The current Finder instance + */ + public function ignoreUnreadableDirs($ignore = true) + { + $this->ignoreUnreadableDirs = (bool) $ignore; + + return $this; + } + + /** + * Searches files and directories which match defined rules. + * + * @param string|array $dirs A directory path or an array of directories + * + * @return Finder The current Finder instance + * + * @throws \InvalidArgumentException if one of the directories does not exist + * + * @api + */ + public function in($dirs) + { + $resolvedDirs = array(); + + foreach ((array) $dirs as $dir) { + if (is_dir($dir)) { + $resolvedDirs[] = $dir; + } elseif ($glob = glob($dir, GLOB_ONLYDIR)) { + $resolvedDirs = array_merge($resolvedDirs, $glob); + } else { + throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); + } + } + + $this->dirs = array_merge($this->dirs, $resolvedDirs); + + return $this; + } + + /** + * Returns an Iterator for the current Finder configuration. + * + * This method implements the IteratorAggregate interface. + * + * @return \Iterator An iterator + * + * @throws \LogicException if the in() method has not been called + */ + public function getIterator() + { + if (0 === count($this->dirs) && 0 === count($this->iterators)) { + throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); + } + + if (1 === count($this->dirs) && 0 === count($this->iterators)) { + return $this->searchInDirectory($this->dirs[0]); + } + + $iterator = new \AppendIterator(); + foreach ($this->dirs as $dir) { + $iterator->append($this->searchInDirectory($dir)); + } + + foreach ($this->iterators as $it) { + $iterator->append($it); + } + + return $iterator; + } + + /** + * Appends an existing set of files/directories to the finder. + * + * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array. + * + * @param mixed $iterator + * + * @return Finder The finder + * + * @throws \InvalidArgumentException When the given argument is not iterable. + */ + public function append($iterator) + { + if ($iterator instanceof \IteratorAggregate) { + $this->iterators[] = $iterator->getIterator(); + } elseif ($iterator instanceof \Iterator) { + $this->iterators[] = $iterator; + } elseif ($iterator instanceof \Traversable || is_array($iterator)) { + $it = new \ArrayIterator(); + foreach ($iterator as $file) { + $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); + } + $this->iterators[] = $it; + } else { + throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); + } + + return $this; + } + + /** + * Counts all the results collected by the iterators. + * + * @return int + */ + public function count() + { + return iterator_count($this->getIterator()); + } + + /** + * @return Finder The current Finder instance + */ + private function sortAdapters() + { + uasort($this->adapters, function (array $a, array $b) { + if ($a['selected'] || $b['selected']) { + return $a['selected'] ? -1 : 1; + } + + return $a['priority'] > $b['priority'] ? -1 : 1; + }); + + return $this; + } + + /** + * @param $dir + * + * @return \Iterator + * + * @throws \RuntimeException When none of the adapters are supported + */ + private function searchInDirectory($dir) + { + if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { + $this->exclude = array_merge($this->exclude, self::$vcsPatterns); + } + + if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { + $this->notPaths[] = '#(^|/)\..+(/|$)#'; + } + + foreach ($this->adapters as $adapter) { + if ($adapter['adapter']->isSupported()) { + try { + return $this + ->buildAdapter($adapter['adapter']) + ->searchInDirectory($dir); + } catch (ExceptionInterface $e) {} + } + } + + throw new \RuntimeException('No supported adapter found.'); + } + + /** + * @param AdapterInterface $adapter + * + * @return AdapterInterface + */ + private function buildAdapter(AdapterInterface $adapter) + { + return $adapter + ->setFollowLinks($this->followLinks) + ->setDepths($this->depths) + ->setMode($this->mode) + ->setExclude($this->exclude) + ->setNames($this->names) + ->setNotNames($this->notNames) + ->setContains($this->contains) + ->setNotContains($this->notContains) + ->setSizes($this->sizes) + ->setDates($this->dates) + ->setFilters($this->filters) + ->setSort($this->sort) + ->setPath($this->paths) + ->setNotPath($this->notPaths) + ->ignoreUnreadableDirs($this->ignoreUnreadableDirs); + } + + /** + * Unselects all adapters. + */ + private function resetAdapterSelection() + { + $this->adapters = array_map(function (array $properties) { + $properties['selected'] = false; + + return $properties; + }, $this->adapters); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php b/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php new file mode 100644 index 000000000..df65ac754 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +/** + * Glob matches globbing patterns against text. + * + * if match_glob("foo.*", "foo.bar") echo "matched\n"; + * + * // prints foo.bar and foo.baz + * $regex = glob_to_regex("foo.*"); + * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t) + * { + * if (/$regex/) echo "matched: $car\n"; + * } + * + * Glob implements glob(3) style matching that can be used to match + * against text, rather than fetching names from a filesystem. + * + * Based on the Perl Text::Glob module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + */ +class Glob +{ + /** + * Returns a regexp which is the equivalent of the glob pattern. + * + * @param string $glob The glob pattern + * @param bool $strictLeadingDot + * @param bool $strictWildcardSlash + * + * @return string regex The regexp + */ + public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true) + { + $firstByte = true; + $escaping = false; + $inCurlies = 0; + $regex = ''; + $sizeGlob = strlen($glob); + for ($i = 0; $i < $sizeGlob; $i++) { + $car = $glob[$i]; + if ($firstByte) { + if ($strictLeadingDot && '.' !== $car) { + $regex .= '(?=[^\.])'; + } + + $firstByte = false; + } + + if ('/' === $car) { + $firstByte = true; + } + + if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { + $regex .= "\\$car"; + } elseif ('*' === $car) { + $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); + } elseif ('?' === $car) { + $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); + } elseif ('{' === $car) { + $regex .= $escaping ? '\\{' : '('; + if (!$escaping) { + ++$inCurlies; + } + } elseif ('}' === $car && $inCurlies) { + $regex .= $escaping ? '}' : ')'; + if (!$escaping) { + --$inCurlies; + } + } elseif (',' === $car && $inCurlies) { + $regex .= $escaping ? ',' : '|'; + } elseif ('\\' === $car) { + if ($escaping) { + $regex .= '\\\\'; + $escaping = false; + } else { + $escaping = true; + } + + continue; + } else { + $regex .= $car; + } + $escaping = false; + } + + return '#^'.$regex.'$#'; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php new file mode 100644 index 000000000..36d79dbc9 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * CustomFilterIterator filters files by applying anonymous functions. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @author Fabien Potencier + */ +class CustomFilterIterator extends FilterIterator +{ + private $filters = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $filters An array of PHP callbacks + * + * @throws \InvalidArgumentException + */ + public function __construct(\Iterator $iterator, array $filters) + { + foreach ($filters as $filter) { + if (!is_callable($filter)) { + throw new \InvalidArgumentException('Invalid PHP callback.'); + } + } + $this->filters = $filters; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + foreach ($this->filters as $filter) { + if (false === call_user_func($filter, $fileinfo)) { + return false; + } + } + + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php new file mode 100644 index 000000000..bad682ee5 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\DateComparator; + +/** + * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates). + * + * @author Fabien Potencier + */ +class DateRangeFilterIterator extends FilterIterator +{ + private $comparators = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param DateComparator[] $comparators An array of DateComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + if (!$fileinfo->isFile()) { + return true; + } + + $filedate = $fileinfo->getMTime(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filedate)) { + return false; + } + } + + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php new file mode 100644 index 000000000..c1c5ce899 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * DepthRangeFilterIterator limits the directory depth. + * + * @author Fabien Potencier + */ +class DepthRangeFilterIterator extends FilterIterator +{ + private $minDepth = 0; + + /** + * Constructor. + * + * @param \RecursiveIteratorIterator $iterator The Iterator to filter + * @param int $minDepth The min depth + * @param int $maxDepth The max depth + */ + public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX) + { + $this->minDepth = $minDepth; + $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + return $this->getInnerIterator()->getDepth() >= $this->minDepth; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php new file mode 100644 index 000000000..b2be58ea7 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * ExcludeDirectoryFilterIterator filters out directories. + * + * @author Fabien Potencier + */ +class ExcludeDirectoryFilterIterator extends FilterIterator +{ + private $patterns = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $directories An array of directories to exclude + */ + public function __construct(\Iterator $iterator, array $directories) + { + foreach ($directories as $directory) { + $this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#'; + } + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); + $path = strtr($path, '\\', '/'); + foreach ($this->patterns as $pattern) { + if (preg_match($pattern, $path)) { + return false; + } + } + + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php new file mode 100644 index 000000000..48634f27d --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\SplFileInfo; + +/** + * Iterate over shell command result. + * + * @author Jean-François Simon + */ +class FilePathsIterator extends \ArrayIterator +{ + /** + * @var string + */ + private $baseDir; + + /** + * @var int + */ + private $baseDirLength; + + /** + * @var string + */ + private $subPath; + + /** + * @var string + */ + private $subPathname; + + /** + * @var SplFileInfo + */ + private $current; + + /** + * @param array $paths List of paths returned by shell command + * @param string $baseDir Base dir for relative path building + */ + public function __construct(array $paths, $baseDir) + { + $this->baseDir = $baseDir; + $this->baseDirLength = strlen($baseDir); + + parent::__construct($paths); + } + + /** + * @param string $name + * @param array $arguments + * + * @return mixed + */ + public function __call($name, array $arguments) + { + return call_user_func_array(array($this->current(), $name), $arguments); + } + + /** + * Return an instance of SplFileInfo with support for relative paths. + * + * @return SplFileInfo File information + */ + public function current() + { + return $this->current; + } + + /** + * @return string + */ + public function key() + { + return $this->current->getPathname(); + } + + public function next() + { + parent::next(); + $this->buildProperties(); + } + + public function rewind() + { + parent::rewind(); + $this->buildProperties(); + } + + /** + * @return string + */ + public function getSubPath() + { + return $this->subPath; + } + + /** + * @return string + */ + public function getSubPathname() + { + return $this->subPathname; + } + + private function buildProperties() + { + $absolutePath = parent::current(); + + if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) { + $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\'); + $dir = dirname($this->subPathname); + $this->subPath = '.' === $dir ? '' : $dir; + } else { + $this->subPath = $this->subPathname = ''; + } + + $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php new file mode 100644 index 000000000..985475e59 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * FileTypeFilterIterator only keeps files, directories, or both. + * + * @author Fabien Potencier + */ +class FileTypeFilterIterator extends FilterIterator +{ + const ONLY_FILES = 1; + const ONLY_DIRECTORIES = 2; + + private $mode; + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) + */ + public function __construct(\Iterator $iterator, $mode) + { + $this->mode = $mode; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { + return false; + } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { + return false; + } + + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php new file mode 100644 index 000000000..9ea333013 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class FilecontentFilterIterator extends MultiplePcreFilterIterator +{ + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + if (!$this->matchRegexps && !$this->noMatchRegexps) { + return true; + } + + $fileinfo = $this->current(); + + if ($fileinfo->isDir() || !$fileinfo->isReadable()) { + return false; + } + + $content = $fileinfo->getContents(); + if (!$content) { + return false; + } + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $content)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $content)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts string to regexp if necessary. + * + * @param string $str Pattern: string or regexp + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php new file mode 100644 index 000000000..c4e0ccd8e --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\Expression\Expression; + +/** + * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string). + * + * @author Fabien Potencier + */ +class FilenameFilterIterator extends MultiplePcreFilterIterator +{ + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $filename = $this->current()->getFilename(); + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts glob to regexp. + * + * PCRE patterns are left unchanged. + * Glob strings are transformed with Glob::toRegex(). + * + * @param string $str Pattern: glob or regexp + * + * @return string regexp corresponding to a given glob or regexp + */ + protected function toRegex($str) + { + return Expression::create($str)->getRegex()->render(); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php new file mode 100644 index 000000000..f4da44c4c --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * This iterator just overrides the rewind method in order to correct a PHP bug. + * + * @see https://bugs.php.net/bug.php?id=49104 + * + * @author Alex Bogomazov + */ +abstract class FilterIterator extends \FilterIterator +{ + /** + * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after + * rewind in some cases. + * + * @see FilterIterator::rewind() + */ + public function rewind() + { + $iterator = $this; + while ($iterator instanceof \OuterIterator) { + $innerIterator = $iterator->getInnerIterator(); + + if ($innerIterator instanceof RecursiveDirectoryIterator) { + if ($innerIterator->isRewindable()) { + $innerIterator->next(); + $innerIterator->rewind(); + } + } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { + $iterator->getInnerIterator()->next(); + $iterator->getInnerIterator()->rewind(); + } + $iterator = $iterator->getInnerIterator(); + } + + parent::rewind(); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php new file mode 100644 index 000000000..d7efc9090 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\Expression\Expression; + +/** + * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings). + * + * @author Fabien Potencier + */ +abstract class MultiplePcreFilterIterator extends FilterIterator +{ + protected $matchRegexps = array(); + protected $noMatchRegexps = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param array $matchPatterns An array of patterns that need to match + * @param array $noMatchPatterns An array of patterns that need to not match + */ + public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) + { + foreach ($matchPatterns as $pattern) { + $this->matchRegexps[] = $this->toRegex($pattern); + } + + foreach ($noMatchPatterns as $pattern) { + $this->noMatchRegexps[] = $this->toRegex($pattern); + } + + parent::__construct($iterator); + } + + /** + * Checks whether the string is a regex. + * + * @param string $str + * + * @return bool Whether the given string is a regex + */ + protected function isRegex($str) + { + return Expression::create($str)->isRegex(); + } + + /** + * Converts string into regexp. + * + * @param string $str Pattern + * + * @return string regexp corresponding to a given string + */ + abstract protected function toRegex($str); +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php new file mode 100644 index 000000000..828fa4187 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * PathFilterIterator filters files by path patterns (e.g. some/special/dir). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class PathFilterIterator extends MultiplePcreFilterIterator +{ + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $filename = $this->current()->getRelativePathname(); + + if (defined('PHP_WINDOWS_VERSION_MAJOR')) { + $filename = strtr($filename, '\\', '/'); + } + + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return false; + } + } + + // should at least match one rule + $match = true; + if ($this->matchRegexps) { + $match = false; + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $filename)) { + return true; + } + } + } + + return $match; + } + + /** + * Converts strings to regexp. + * + * PCRE patterns are left unchanged. + * + * Default conversion: + * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/' + * + * Use only / as directory separator (on Windows also). + * + * @param string $str Pattern: regexp or dirname. + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php new file mode 100644 index 000000000..9543a3f76 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\Exception\AccessDeniedException; +use Symfony\Component\Finder\SplFileInfo; + +/** + * Extends the \RecursiveDirectoryIterator to support relative paths + * + * @author Victor Berchet + */ +class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator +{ + /** + * @var bool + */ + private $ignoreUnreadableDirs; + + /** + * @var bool + */ + private $rewindable; + + /** + * Constructor. + * + * @param string $path + * @param int $flags + * @param bool $ignoreUnreadableDirs + * + * @throws \RuntimeException + */ + public function __construct($path, $flags, $ignoreUnreadableDirs = false) + { + if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { + throw new \RuntimeException('This iterator only support returning current as fileinfo.'); + } + + parent::__construct($path, $flags); + $this->ignoreUnreadableDirs = $ignoreUnreadableDirs; + } + + /** + * Return an instance of SplFileInfo with support for relative paths + * + * @return SplFileInfo File information + */ + public function current() + { + return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname()); + } + + /** + * @return \RecursiveIterator + * + * @throws AccessDeniedException + */ + public function getChildren() + { + try { + $children = parent::getChildren(); + + if ($children instanceof self) { + // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore + $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs; + } + + return $children; + } catch (\UnexpectedValueException $e) { + if ($this->ignoreUnreadableDirs) { + // If directory is unreadable and finder is set to ignore it, a fake empty content is returned. + return new \RecursiveArrayIterator(array()); + } else { + throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); + } + } + } + + /** + * Do nothing for non rewindable stream + */ + public function rewind() + { + if (false === $this->isRewindable()) { + return; + } + + // @see https://bugs.php.net/bug.php?id=49104 + parent::next(); + + parent::rewind(); + } + + /** + * Checks if the stream is rewindable. + * + * @return bool true when the stream is rewindable, false otherwise + */ + public function isRewindable() + { + if (null !== $this->rewindable) { + return $this->rewindable; + } + + if (false !== $stream = @opendir($this->getPath())) { + $infos = stream_get_meta_data($stream); + closedir($stream); + + if ($infos['seekable']) { + return $this->rewindable = true; + } + } + + return $this->rewindable = false; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php new file mode 100644 index 000000000..b345a1692 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\NumberComparator; + +/** + * SizeRangeFilterIterator filters out files that are not in the given size range. + * + * @author Fabien Potencier + */ +class SizeRangeFilterIterator extends FilterIterator +{ + private $comparators = array(); + + /** + * Constructor. + * + * @param \Iterator $iterator The Iterator to filter + * @param NumberComparator[] $comparators An array of NumberComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (!$fileinfo->isFile()) { + return true; + } + + $filesize = $fileinfo->getSize(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filesize)) { + return false; + } + } + + return true; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php new file mode 100644 index 000000000..3bf5034d1 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * SortableIterator applies a sort on a given Iterator. + * + * @author Fabien Potencier + */ +class SortableIterator implements \IteratorAggregate +{ + const SORT_BY_NAME = 1; + const SORT_BY_TYPE = 2; + const SORT_BY_ACCESSED_TIME = 3; + const SORT_BY_CHANGED_TIME = 4; + const SORT_BY_MODIFIED_TIME = 5; + + private $iterator; + private $sort; + + /** + * Constructor. + * + * @param \Traversable $iterator The Iterator to filter + * @param int|callback $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) + * + * @throws \InvalidArgumentException + */ + public function __construct(\Traversable $iterator, $sort) + { + $this->iterator = $iterator; + + if (self::SORT_BY_NAME === $sort) { + $this->sort = function ($a, $b) { + return strcmp($a->getRealpath(), $b->getRealpath()); + }; + } elseif (self::SORT_BY_TYPE === $sort) { + $this->sort = function ($a, $b) { + if ($a->isDir() && $b->isFile()) { + return -1; + } elseif ($a->isFile() && $b->isDir()) { + return 1; + } + + return strcmp($a->getRealpath(), $b->getRealpath()); + }; + } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getATime() > $b->getATime()); + }; + } elseif (self::SORT_BY_CHANGED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getCTime() > $b->getCTime()); + }; + } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { + $this->sort = function ($a, $b) { + return ($a->getMTime() > $b->getMTime()); + }; + } elseif (is_callable($sort)) { + $this->sort = $sort; + } else { + throw new \InvalidArgumentException('The SortableIterator takes a PHP callback or a valid built-in sort algorithm as an argument.'); + } + } + + public function getIterator() + { + $array = iterator_to_array($this->iterator, true); + uasort($array, $this->sort); + + return new \ArrayIterator($array); + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE b/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE new file mode 100644 index 000000000..0b3292cf9 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2014 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/README.md b/core/lib/symfony/finder/Symfony/Component/Finder/README.md new file mode 100644 index 000000000..8494531d2 --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/README.md @@ -0,0 +1,41 @@ +Finder Component +================ + +Finder finds files and directories via an intuitive fluent interface. + + use Symfony\Component\Finder\Finder; + + $finder = new Finder(); + + $iterator = $finder + ->files() + ->name('*.php') + ->depth(0) + ->size('>= 1K') + ->in(__DIR__); + + foreach ($iterator as $file) { + print $file->getRealpath()."\n"; + } + +But you can also use it to find files stored remotely like in this example where +we are looking for files on Amazon S3: + + $s3 = new \Zend_Service_Amazon_S3($key, $secret); + $s3->registerStreamWrapper("s3"); + + $finder = new Finder(); + $finder->name('photos*')->size('< 100K')->date('since 1 hour ago'); + foreach ($finder->in('s3://bucket-name') as $file) { + print $file->getFilename()."\n"; + } + +Resources +--------- + +You can run the unit tests with the following command: + + $ cd path/to/Symfony/Component/Finder/ + $ composer.phar install + $ phpunit + diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php new file mode 100644 index 000000000..78824d0df --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php @@ -0,0 +1,294 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Shell; + +/** + * @author Jean-François Simon + */ +class Command +{ + /** + * @var Command|null + */ + private $parent; + + /** + * @var array + */ + private $bits = array(); + + /** + * @var array + */ + private $labels = array(); + + /** + * @var \Closure|null + */ + private $errorHandler; + + /** + * Constructor. + * + * @param Command|null $parent Parent command + */ + public function __construct(Command $parent = null) + { + $this->parent = $parent; + } + + /** + * Returns command as string. + * + * @return string + */ + public function __toString() + { + return $this->join(); + } + + /** + * Creates a new Command instance. + * + * @param Command|null $parent Parent command + * + * @return Command New Command instance + */ + public static function create(Command $parent = null) + { + return new self($parent); + } + + /** + * Escapes special chars from input. + * + * @param string $input A string to escape + * + * @return string The escaped string + */ + public static function escape($input) + { + return escapeshellcmd($input); + } + + /** + * Quotes input. + * + * @param string $input An argument string + * + * @return string The quoted string + */ + public static function quote($input) + { + return escapeshellarg($input); + } + + /** + * Appends a string or a Command instance. + * + * @param string|Command $bit + * + * @return Command The current Command instance + */ + public function add($bit) + { + $this->bits[] = $bit; + + return $this; + } + + /** + * Prepends a string or a command instance. + * + * @param string|Command $bit + * + * @return Command The current Command instance + */ + public function top($bit) + { + array_unshift($this->bits, $bit); + + foreach ($this->labels as $label => $index) { + $this->labels[$label] += 1; + } + + return $this; + } + + /** + * Appends an argument, will be quoted. + * + * @param string $arg + * + * @return Command The current Command instance + */ + public function arg($arg) + { + $this->bits[] = self::quote($arg); + + return $this; + } + + /** + * Appends escaped special command chars. + * + * @param string $esc + * + * @return Command The current Command instance + */ + public function cmd($esc) + { + $this->bits[] = self::escape($esc); + + return $this; + } + + /** + * Inserts a labeled command to feed later. + * + * @param string $label The unique label + * + * @return Command The current Command instance + * + * @throws \RuntimeException If label already exists + */ + public function ins($label) + { + if (isset($this->labels[$label])) { + throw new \RuntimeException(sprintf('Label "%s" already exists.', $label)); + } + + $this->bits[] = self::create($this); + $this->labels[$label] = count($this->bits)-1; + + return $this->bits[$this->labels[$label]]; + } + + /** + * Retrieves a previously labeled command. + * + * @param string $label + * + * @return Command The labeled command + * + * @throws \RuntimeException + */ + public function get($label) + { + if (!isset($this->labels[$label])) { + throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label)); + } + + return $this->bits[$this->labels[$label]]; + } + + /** + * Returns parent command (if any). + * + * @return Command Parent command + * + * @throws \RuntimeException If command has no parent + */ + public function end() + { + if (null === $this->parent) { + throw new \RuntimeException('Calling end on root command doesn\'t make sense.'); + } + + return $this->parent; + } + + /** + * Counts bits stored in command. + * + * @return int The bits count + */ + public function length() + { + return count($this->bits); + } + + /** + * @param \Closure $errorHandler + * + * @return Command + */ + public function setErrorHandler(\Closure $errorHandler) + { + $this->errorHandler = $errorHandler; + + return $this; + } + + /** + * @return \Closure|null + */ + public function getErrorHandler() + { + return $this->errorHandler; + } + + /** + * Executes current command. + * + * @return array The command result + * + * @throws \RuntimeException + */ + public function execute() + { + if (null === $this->errorHandler) { + exec($this->join(), $output); + } else { + $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); + $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY); + + if ($error = stream_get_contents($pipes[2])) { + call_user_func($this->errorHandler, $error); + } + + proc_close($process); + } + + return $output ?: array(); + } + + /** + * Joins bits. + * + * @return string + */ + public function join() + { + return implode(' ', array_filter( + array_map(function ($bit) { + return $bit instanceof Command ? $bit->join() : ($bit ?: null); + }, $this->bits), + function ($bit) { return null !== $bit; } + )); + } + + /** + * Insert a string or a Command instance before the bit at given position $index (index starts from 0). + * + * @param string|Command $bit + * @param int $index + * + * @return Command The current Command instance + */ + public function addAtIndex($bit, $index) + { + array_splice($this->bits, $index, 0, $bit); + + return $this; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php new file mode 100644 index 000000000..8586cbc3e --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Shell; + +/** + * @author Jean-François Simon + */ +class Shell +{ + const TYPE_UNIX = 1; + const TYPE_DARWIN = 2; + const TYPE_CYGWIN = 3; + const TYPE_WINDOWS = 4; + const TYPE_BSD = 5; + + /** + * @var string|null + */ + private $type; + + /** + * Returns guessed OS type. + * + * @return int + */ + public function getType() + { + if (null === $this->type) { + $this->type = $this->guessType(); + } + + return $this->type; + } + + /** + * Tests if a command is available. + * + * @param string $command + * + * @return bool + */ + public function testCommand($command) + { + if (self::TYPE_WINDOWS === $this->type) { + // todo: find a way to test if Windows command exists + return false; + } + + if (!function_exists('exec')) { + return false; + } + + // todo: find a better way (command could not be available) + exec('command -v '.$command, $output, $code); + + return 0 === $code && count($output) > 0; + } + + /** + * Guesses OS type. + * + * @return int + */ + private function guessType() + { + $os = strtolower(PHP_OS); + + if (false !== strpos($os, 'cygwin')) { + return self::TYPE_CYGWIN; + } + + if (false !== strpos($os, 'darwin')) { + return self::TYPE_DARWIN; + } + + if (false !== strpos($os, 'bsd')) { + return self::TYPE_BSD; + } + + if (0 === strpos($os, 'win')) { + return self::TYPE_WINDOWS; + } + + return self::TYPE_UNIX; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php b/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php new file mode 100644 index 000000000..d911fe06e --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +/** + * Extends \SplFileInfo to support relative paths + * + * @author Fabien Potencier + */ +class SplFileInfo extends \SplFileInfo +{ + private $relativePath; + private $relativePathname; + + /** + * Constructor + * + * @param string $file The file name + * @param string $relativePath The relative path + * @param string $relativePathname The relative path name + */ + public function __construct($file, $relativePath, $relativePathname) + { + parent::__construct($file); + $this->relativePath = $relativePath; + $this->relativePathname = $relativePathname; + } + + /** + * Returns the relative path + * + * @return string the relative path + */ + public function getRelativePath() + { + return $this->relativePath; + } + + /** + * Returns the relative path name + * + * @return string the relative path name + */ + public function getRelativePathname() + { + return $this->relativePathname; + } + + /** + * Returns the contents of the file + * + * @return string the contents of the file + * + * @throws \RuntimeException + */ + public function getContents() + { + $level = error_reporting(0); + $content = file_get_contents($this->getPathname()); + error_reporting($level); + if (false === $content) { + $error = error_get_last(); + throw new \RuntimeException($error['message']); + } + + return $content; + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/composer.json b/core/lib/symfony/finder/Symfony/Component/Finder/composer.json new file mode 100644 index 000000000..7de47137f --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/finder", + "type": "library", + "description": "Symfony Finder Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Finder\\": "" } + }, + "target-dir": "Symfony/Component/Finder", + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + } +} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist b/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist new file mode 100644 index 000000000..23272235b --- /dev/null +++ b/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist @@ -0,0 +1,29 @@ + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + From 743b0c262e3b20e53c113a067b728cf1284fa70f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:13:14 -0400 Subject: [PATCH 084/166] updated composer --- composer.json | 3 +- composer.lock | 97 +++++++++++++++++++++- core/lib/autoload.php | 2 +- core/lib/composer/autoload_namespaces.php | 2 + core/lib/composer/autoload_real.php | 8 +- core/lib/composer/installed.json | 99 +++++++++++++++++++++++ 6 files changed, 204 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 3e52e19e2..75f5b3bdf 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "require": { "alchemy/zippy": "0.2.0", "michelf/php-markdown": "1.4", - "symfony/yaml": "2.4.4" + "symfony/yaml": "2.4.4", + "scan/kss-php": "v0.6.0" } } diff --git a/composer.lock b/composer.lock index e4f4337ae..8e1cf3147 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "27deb92c30ef38ddb06e420b9f908f1c", + "hash": "3d9707b037b50d1292b7def1040f4095", "packages": [ { "name": "alchemy/zippy", @@ -326,6 +326,52 @@ ], "time": "2013-11-22 08:30:29" }, + { + "name": "scan/kss-php", + "version": "v0.6.0", + "source": { + "type": "git", + "url": "https://github.com/scaninc/kss-php.git", + "reference": "5acddff2758761252d8e6da97680ef1f412457cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/scaninc/kss-php/zipball/5acddff2758761252d8e6da97680ef1f412457cb", + "reference": "5acddff2758761252d8e6da97680ef1f412457cb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/finder": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "Scan\\Kss": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Russell Ahlstrom", + "email": "russell.ahlstrom@gmail.com", + "homepage": "http://russell.ahlstromology.com" + } + ], + "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", + "keywords": [ + "css documentation", + "kss", + "styleguide" + ], + "time": "2013-11-11 17:49:11" + }, { "name": "symfony/event-dispatcher", "version": "v2.4.4", @@ -431,6 +477,55 @@ "homepage": "http://symfony.com", "time": "2014-04-16 10:34:31" }, + { + "name": "symfony/finder", + "version": "v2.4.4", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/25e1e7d5e7376f8a92ae3b1d714d956edf33a730", + "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com", + "time": "2014-04-27 13:34:57" + }, { "name": "symfony/process", "version": "v2.4.4", diff --git a/core/lib/autoload.php b/core/lib/autoload.php index cbe1894d6..5e8f5e9e9 100644 --- a/core/lib/autoload.php +++ b/core/lib/autoload.php @@ -4,4 +4,4 @@ require_once __DIR__ . '/composer' . '/autoload_real.php'; -return ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf::getLoader(); +return ComposerAutoloaderInit30ec8539c8312241b2e6d5315067bda6::getLoader(); diff --git a/core/lib/composer/autoload_namespaces.php b/core/lib/composer/autoload_namespaces.php index 9dce742c5..fbcf3f100 100644 --- a/core/lib/composer/autoload_namespaces.php +++ b/core/lib/composer/autoload_namespaces.php @@ -8,8 +8,10 @@ return array( 'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'), 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), + 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Scan\\Kss' => array($vendorDir . '/scan/kss-php/lib'), 'Pimple' => array($vendorDir . '/pimple/pimple/lib'), 'Michelf' => array($vendorDir . '/michelf/php-markdown'), 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), diff --git a/core/lib/composer/autoload_real.php b/core/lib/composer/autoload_real.php index 67493fbbf..db7b7a0a0 100644 --- a/core/lib/composer/autoload_real.php +++ b/core/lib/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf +class ComposerAutoloaderInit30ec8539c8312241b2e6d5315067bda6 { private static $loader; @@ -19,9 +19,9 @@ public static function getLoader() return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit30ec8539c8312241b2e6d5315067bda6', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); - spl_autoload_unregister(array('ComposerAutoloaderInit6d5344d88a6339674b66b055e9491daf', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit30ec8539c8312241b2e6d5315067bda6', 'loadClassLoader')); $vendorDir = dirname(__DIR__); $baseDir = dirname(dirname($vendorDir)); @@ -47,7 +47,7 @@ public static function getLoader() } } -function composerRequire6d5344d88a6339674b66b055e9491daf($file) +function composerRequire30ec8539c8312241b2e6d5315067bda6($file) { require $file; } diff --git a/core/lib/composer/installed.json b/core/lib/composer/installed.json index 41173b449..8f15061b2 100644 --- a/core/lib/composer/installed.json +++ b/core/lib/composer/installed.json @@ -540,5 +540,104 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com" + }, + { + "name": "symfony/finder", + "version": "v2.4.4", + "version_normalized": "2.4.4.0", + "target-dir": "Symfony/Component/Finder", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/25e1e7d5e7376f8a92ae3b1d714d956edf33a730", + "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2014-04-27 13:34:57", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "http://symfony.com" + }, + { + "name": "scan/kss-php", + "version": "v0.6.0", + "version_normalized": "0.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/scaninc/kss-php.git", + "reference": "5acddff2758761252d8e6da97680ef1f412457cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/scaninc/kss-php/zipball/5acddff2758761252d8e6da97680ef1f412457cb", + "reference": "5acddff2758761252d8e6da97680ef1f412457cb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/finder": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "time": "2013-11-11 17:49:11", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Scan\\Kss": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Russell Ahlstrom", + "email": "russell.ahlstrom@gmail.com", + "homepage": "http://russell.ahlstromology.com" + } + ], + "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", + "keywords": [ + "css documentation", + "kss", + "styleguide" + ] } ] From 180086a97562566516cb517653354a53c3000016 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:13:41 -0400 Subject: [PATCH 085/166] simple PL KSS library --- core/lib/PatternLab/KSSParser.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 core/lib/PatternLab/KSSParser.php diff --git a/core/lib/PatternLab/KSSParser.php b/core/lib/PatternLab/KSSParser.php new file mode 100644 index 000000000..3f4084ddc --- /dev/null +++ b/core/lib/PatternLab/KSSParser.php @@ -0,0 +1,24 @@ + Date: Tue, 20 May 2014 14:14:38 -0400 Subject: [PATCH 086/166] kss support --- core/lib/PatternLab/Builder.php | 57 +++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index db3d85caa..ef5e4dd12 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -86,7 +86,7 @@ public function __construct($config = array()) { * * @return {String} the mark-up as rendered by Mustache */ - protected function renderPattern($f,$p) { + protected function renderPattern($f,$p,$k = array()) { // if there is pattern-specific data make sure to override the default in $this->d $d = $this->d; @@ -114,8 +114,12 @@ protected function renderPattern($f,$p) { } - $pattern = $this->pl->render($f,$d); - $escaped = htmlentities($pattern); + if (count($k) > 0) { + $d = array_merge($d, $k); + } + + $pattern = $this->pl->render($f,$d); + $escaped = htmlentities($pattern); if ($this->addPatternHF) { $patternHead = $this->mv->render($this->patternHead,$d); @@ -509,6 +513,8 @@ protected function gatherPatternInfo() { PatternInfo::loadRules($options); PatternInfo::gather($options); + $kss = KSSParser::parse($this->sd); + // initialize various arrays $this->navItems = PatternInfo::$navItems; $this->patternPaths = PatternInfo::$patternPaths; @@ -631,6 +637,16 @@ protected function gatherPatternInfo() { // add patterns to $this->patternPartials foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItem) { + $patternSectionVanilla = true; + $patternSectionKSS = false; + + // see if this is in KSS + $kssSection = $kss->getSection($patternSubtypeItem["patternPartial"]); + if ($kssSection) { + $patternSectionVanilla = false; + $patternSectionKSS = true; + } + $patternCode = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); $patternCodeRaw = $patternCode[0]; $patternCodeEncoded = $patternCode[1]; @@ -638,6 +654,7 @@ protected function gatherPatternInfo() { $patternLineages = $this->patternLineages[$patternSubtypeItem["patternPartial"]]; $patternLineageRExists = (count($this->patternLineagesR[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; $patternLineagesR = $this->patternLineagesR[$patternSubtypeItem["patternPartial"]]; + $patternLineageEExists = ($patternLineageExists || $patternLineageRExists) ? true : false; // set-up the mark-up for CSS Rule Saver so it can figure out which rules to save $patternCSSExists = $this->enableCSS; @@ -648,7 +665,8 @@ protected function gatherPatternInfo() { $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; } - $patternPartialData = array("patternSectionVanilla" => true, + $patternPartialData = array("patternSectionVanilla" => $patternSectionVanilla, + "patternSectionKSS" => $patternSectionKSS, "patternName" => $patternSubtypeItem["patternName"], "patternLink" => $patternSubtypeItem["patternPath"], "patternPartial" => $patternSubtypeItem["patternPartial"], @@ -659,8 +677,8 @@ protected function gatherPatternInfo() { "patternLineageExists" => $patternLineageExists, "patternLineages" => $patternLineages, "patternLineageRExists" => $patternLineageRExists, - "patternLineagesR" => $patternLineagesR - ); + "patternLineagesR" => $patternLineagesR, + "patternLineageEExists" => $patternLineageEExists); if (isset($patternSubtypeItem["patternDesc"])) { $patternPartialData["patternDesc"] = $patternSubtypeItem["patternDesc"]; @@ -670,6 +688,33 @@ protected function gatherPatternInfo() { $patternPartialData["patternMeta"] = $patternSubtypeItem["patternMeta"]; } + if ($kssSection) { + $patternPartialData["patternName"] = $kssSection->getTitle(); + $patternPartialData["patternDesc"] = $kssSection->getDescription(); + $modifiers = $kssSection->getModifiers(); + if (count($modifiers) > 0) { + $patternPartialData["patternModifiersExist"] = true; + $patternPartialData["patternModifiers"] = array(); + foreach ($modifiers as $modifier) { + $name = $modifier->getName(); + $class = $modifier->getClassName(); + $desc = $modifier->getDescription(); + $code = ""; + $patternModifierCodeExists = false; + if ($name[0] != ":") { + list($code,$orig) = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"],array("styleModifier" => $class)); + $patternModifierCodeExists = true; + } + $patternPartialData["patternModifiers"][] = array("patternModifierName" => $name, + "patternModifierDesc" => $desc, + "patternModifierCode" => $code, + "patternModifierCodeExists" => $patternModifierCodeExists); + } + } + } + + $patternPartialData["patternDescExists"] = isset($patternPartialData["patternDesc"]); + $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = $patternPartialData; // set the pattern state From 707d532e32305aef4ef68e6262049466af63b1e4 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:15:10 -0400 Subject: [PATCH 087/166] updating the view all w/ rough KSS output support --- .../partials/patternSection.mustache | 91 +++++++++++++++++++ .../partials/patternSectionSubtype.mustache | 6 ++ core/templates/viewall.mustache | 38 ++------ 3 files changed, 104 insertions(+), 31 deletions(-) create mode 100644 core/templates/partials/patternSection.mustache create mode 100644 core/templates/partials/patternSectionSubtype.mustache diff --git a/core/templates/partials/patternSection.mustache b/core/templates/partials/patternSection.mustache new file mode 100644 index 000000000..42cc624a2 --- /dev/null +++ b/core/templates/partials/patternSection.mustache @@ -0,0 +1,91 @@ +
    +
    +

    {{ patternName }}

    +
    + Pattern header options +
    +
    + {{# patternDescExists }} +
    + {{{ patternDesc }}} + {{# patternModifiersExist }} +
      + {{# patternModifiers }} +
    • {{ patternModifierName }}: {{ patternModifierDesc }}
    • + {{/ patternModifiers }} +
    + {{/ patternModifiersExist }} +
    + {{/ patternDescExists }} +
    + {{{ patternPartialCode }}} +
    + {{# patternModifiersExist }} + {{# patternModifiers }} + {{# patternModifierCodeExists }} +
    + {{ patternModifierName }} + {{{ patternModifierCode }}} +
    + {{/ patternModifierCodeExists }} + {{/ patternModifiers }} + {{/ patternModifiersExist }} +
    + + + + + {{# patternCSSExists }} + + {{/ patternCSSExists }} +
    +
    \ No newline at end of file diff --git a/core/templates/partials/patternSectionSubtype.mustache b/core/templates/partials/patternSectionSubtype.mustache new file mode 100644 index 000000000..56fa1b855 --- /dev/null +++ b/core/templates/partials/patternSectionSubtype.mustache @@ -0,0 +1,6 @@ +
    +

    {{ patternName }}

    +
    + {{{ patternDesc }}} +
    +
    \ No newline at end of file diff --git a/core/templates/viewall.mustache b/core/templates/viewall.mustache index 4d278ac11..8daaf4460 100644 --- a/core/templates/viewall.mustache +++ b/core/templates/viewall.mustache @@ -5,39 +5,15 @@
    {{# partials }} -
    -

    {{ patternName }}

    -
    - {{{ patternPartialCode }}} - -
    -
    + {{# patternSectionSubtype }} + {{> patternSectionSubtype }} + {{/ patternSectionSubtype }} + {{^ patternSectionSubtype }} + {{> patternSection }} + {{/ patternSectionSubtype }} {{/ partials }}
    - +
    From 556ad60ffb9b3296646a43b3e0af256c6704b65d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:15:28 -0400 Subject: [PATCH 088/166] tweaking how the pattern footer is populated --- core/lib/PatternLab/Builder.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index ef5e4dd12..efa309624 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -246,20 +246,22 @@ protected function generatePatterns() { * @param {String} path where the files need to be written too * @param {String} pattern state */ - private function generatePatternFile($f,$p,$path,$state) { - - // render the pattern and return it as well as the encoded version - list($rf,$e) = $this->renderPattern($f,$p); + private function generatePatternFile($f,$p,$path,$options) { // the core footer isn't rendered as mustache but we have some variables there any way. find & replace. - $rf = str_replace("{% patternPartial %}",$p,$rf); - $rf = str_replace("{% lineage %}",json_encode($this->patternLineages[$p]),$rf); - $rf = str_replace("{% lineageR %}",json_encode($this->patternLineagesR[$p]),$rf); - $rf = str_replace("{% patternState %}",$state,$rf); + $patternFooterData = array("patternFooterData" => array()); + $patternFooterData["patternFooterData"]["cssEnabled"] = ($this->enableCSS && isset($this->patternCSS[$p])) ? "true" : "false"; + $patternFooterData["patternFooterData"]["lineage"] = json_encode($this->patternLineages[$p]); + $patternFooterData["patternFooterData"]["lineageR"] = json_encode($this->patternLineagesR[$p]); + $patternFooterData["patternFooterData"]["patternBreadcrumb"] = $options["patternBreadcrumb"]; + $patternFooterData["patternFooterData"]["patternDesc"] = $options["patternDesc"]; + $patternFooterData["patternFooterData"]["patternEngine"] = $options["patternEngine"]; + $patternFooterData["patternFooterData"]["patternName"] = $options["patternName"]; + $patternFooterData["patternFooterData"]["patternPartial"] = $p; + $patternFooterData["patternFooterData"]["patternState"] = $options["state"]; - // figure out what to put in the css section - $c = $this->enableCSS && isset($this->patternCSS[$p]) ? "true" : "false"; - $rf = str_replace("{% cssEnabled %}",$c,$rf); + // render the pattern and return it as well as the encoded version + list($rf,$e) = $this->renderPattern($f,$p,$patternFooterData); // get the original mustache template $m = htmlentities(file_get_contents(__DIR__.$this->sp.$f)); @@ -375,7 +377,7 @@ protected function gatherData() { } if (is_array($this->d)) { - $reservedKeys = array("listItems","cacheBuster","link","patternSpecific"); + $reservedKeys = array("listItems","cacheBuster","link","patternSpecific","patternFooterData"); foreach ($reservedKeys as $reservedKey) { if (array_key_exists($reservedKey,$this->d)) { print "\"".$reservedKey."\" is a reserved key in Pattern Lab. The data using that key in _data.json will be overwritten. Please choose a new key.\n"; @@ -749,7 +751,7 @@ protected function gatherPatternInfo() { } // add pattern lab's resource to the user-defined files - $templateHelper = new TemplateHelper($this->sp); + $templateHelper = new TemplateHelper($this->sp); $this->patternHead = $templateHelper->patternHead; $this->patternFoot = $templateHelper->patternFoot; $this->mainPageHead = $templateHelper->mainPageHead; From 21b949e6789efe41886c7e72611528c19631901a Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:15:48 -0400 Subject: [PATCH 089/166] adding new attributes in the footer --- .../pattern-header-footer/footer-pattern.html | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/templates/pattern-header-footer/footer-pattern.html b/core/templates/pattern-header-footer/footer-pattern.html index 4ad384a8c..16a207954 100644 --- a/core/templates/pattern-header-footer/footer-pattern.html +++ b/core/templates/pattern-header-footer/footer-pattern.html @@ -1,10 +1,14 @@ \ No newline at end of file From 3ccba5bfcc74aa6bc05192e01e9aab6e926acc87 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 20 May 2014 14:15:55 -0400 Subject: [PATCH 090/166] copy edit --- core/lib/PatternLab/TemplateHelper.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/lib/PatternLab/TemplateHelper.php b/core/lib/PatternLab/TemplateHelper.php index 827d7a4ad..ee01e5939 100644 --- a/core/lib/PatternLab/TemplateHelper.php +++ b/core/lib/PatternLab/TemplateHelper.php @@ -25,13 +25,13 @@ class TemplateHelper { public function __construct($sp) { // load pattern-lab's resources - $htmlHead = file_get_contents(__DIR__."/../../templates/pattern-header-footer/header.html"); - $htmlFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer.html"); - $extraFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer-pattern.html"); + $htmlHead = file_get_contents(__DIR__."/../../templates/pattern-header-footer/header.html"); + $htmlFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer.html"); + $extraFoot = file_get_contents(__DIR__."/../../templates/pattern-header-footer/footer-pattern.html"); // gather the user-defined header and footer information - $patternHeadPath = __DIR__.$sp."00-atoms/00-meta/_00-head.mustache"; - $patternFootPath = __DIR__.$sp."00-atoms/00-meta/_01-foot.mustache"; + $patternHeadPath = __DIR__.$sp."00-atoms/00-meta/_00-head.mustache"; + $patternFootPath = __DIR__.$sp."00-atoms/00-meta/_01-foot.mustache"; $patternHead = (file_exists($patternHeadPath)) ? file_get_contents($patternHeadPath) : ""; $patternFoot = (file_exists($patternFootPath)) ? file_get_contents($patternFootPath) : ""; From 732a812cd2162a0bbb647fd3b5a9a8bcc2ed0e46 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:34:43 -0400 Subject: [PATCH 091/166] renaming the config option that sets the pattern engine --- core/config/config.ini.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/config/config.ini.default b/core/config/config.ini.default index 5ef75ef2d..58d57c38b 100644 --- a/core/config/config.ini.default +++ b/core/config/config.ini.default @@ -43,7 +43,7 @@ styleGuideExcludes = "" cacheBusterOn = "true" // the pattern rending engine -patternEngine = "mustache" +patternExtension = "mustache" // the public directory publicDir = "public" From 6d145d5034f962008d69dd965133426e22ca0b45 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:35:28 -0400 Subject: [PATCH 092/166] renaming and making the config class static --- core/lib/PatternLab/Config.php | 159 +++++++++++++++++++++++++++++ core/lib/PatternLab/Configurer.php | 113 -------------------- 2 files changed, 159 insertions(+), 113 deletions(-) create mode 100644 core/lib/PatternLab/Config.php delete mode 100644 core/lib/PatternLab/Configurer.php diff --git a/core/lib/PatternLab/Config.php b/core/lib/PatternLab/Config.php new file mode 100644 index 000000000..444c0ff65 --- /dev/null +++ b/core/lib/PatternLab/Config.php @@ -0,0 +1,159 @@ +migrate(true); + if ($migrate) { + if (!@copy(self::$plConfigPath, self::$userConfigPath)) { + print "Please make sure that Pattern Lab can write a new config to config/.\n"; + exit; + } + } else { + self::$options = self::writeNewConfigFile(self::$options,$defaultOptions); + } + } + + // making sure the config isn't empty + if (empty(self::$options)) { + print "A set of configuration options is required to use Pattern Lab.\n"; + exit; + } + + // set-up the source & public dirs + self::$options["sourceDir"] = rtrim(self::$options["sourceDir"],"\\"); + self::$options["publicDir"] = rtrim(self::$options["publicDir"],"\\"); + self::$options["patternSourceDir"] = "/../../../".self::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; + self::$options["patternPublicDir"] = "/../../../".self::$options["publicDir"]."/patterns".DIRECTORY_SEPARATOR; + self::$options["sourceDir"] = __DIR__."/../../../".self::$options["sourceDir"]; + self::$options["publicDir"] = __DIR__."/../../../".self::$options["publicDir"]; + + // populate some standard variables out of the config + foreach (self::$options as $key => $value) { + + // if the variables are array-like make sure the properties are validated/trimmed/lowercased before saving + if (in_array($key,self::$cleanValues)) { + $values = explode(",",$value); + array_walk($values,'PatternLab\Util::trim'); + self::$options[$key] = $values; + } else if ($key == "ishControlsHide") { + self::$options[$key] = new \stdClass(); + $class = self::$options[$key]; + if ($value != "") { + $values = explode(",",$value); + foreach($values as $value2) { + $value2 = trim($value2); + $class->$value2 = true; + } + } + if (self::$options["pageFollowNav"] == "false") { + $value = "tools-follow"; + $class->$value = true; + } + if (self::$options["autoReloadNav"] == "false") { + $value = "tools-reload"; + $class->$value = true; + } + $toolssnapshot = "tools-snapshot"; // i was an idgit and used dashes + if (!isset($class->$toolssnapshot)) { + if (!is_dir(self::$options["patternSourceDir"]."/snapshots")) { + $class->$toolssnapshot = true; + } + } + } + + } + + // set the cacheBuster + self::$options["cacheBuster"] = (self::$options["cacheBusterOn"] == "false") ? 0 : time(); + + // provide the default for enable CSS. performance hog so it should be run infrequently + self::$options["enableCSS"] = false; + + } + + /** + * Use the default config as a base and update it with old config options. Write out a new user config. + * @param {Array} the old configuration file options + * @param {Array} the default configuration file options + * + * @return {Array} the new configuration + */ + protected static function writeNewConfigFile($oldOptions,$defaultOptions) { + + // iterate over the old config and replace values in the new config + foreach ($oldOptions as $key => $value) { + if ($key != "v") { + $defaultOptions[$key] = $value; + } + } + + // create the output data + $configOutput = ""; + foreach ($defaultOptions as $key => $value) { + $configOutput .= $key." = \"".$value."\"\n"; + } + + // write out the new config file + file_put_contents(self::$userConfigPath,$configOutput); + + return $defaultOptions; + + } + +} diff --git a/core/lib/PatternLab/Configurer.php b/core/lib/PatternLab/Configurer.php deleted file mode 100644 index 292c66507..000000000 --- a/core/lib/PatternLab/Configurer.php +++ /dev/null @@ -1,113 +0,0 @@ -userConfigPath = __DIR__."/../../../config/config.ini"; - $this->plConfigPath = __DIR__."/../../config/config.ini.default"; - - } - - /** - * Returns the appropriate config. If it's an old version it updates the config and runs migrations - * @param {String} the version number for Pattern Lab from builder.php - * - * @return {Array} the configuration - */ - public function getConfig() { - - // make sure migrate doesn't happen by default - $migrate = false; - $diffVersion = false; - - // double-check the default config file exists - if (!file_exists($this->plConfigPath)) { - print "Please make sure config.ini.default exists before trying to have Pattern Lab build the config.ini file automagically.\n"; - exit; - } - - // set the default config using the pattern lab config - $config = parse_ini_file($this->plConfigPath); - $defaultConfig = $config; - - // check to see if the user config exists, if not create it - print "configuring pattern lab...\n"; - if (!file_exists($this->userConfigPath)) { - $migrate = true; - } else { - $config = parse_ini_file($this->userConfigPath); - } - - // compare version numbers - $diffVersion = ($config["v"] != $defaultConfig["v"]) ? true : false; - - // run an upgrade and migrations if necessary - if ($migrate || $diffVersion) { - print "upgrading your version of pattern lab...\n"; - print "checking for migrations...\n"; - $m = new Migrator; - $m->migrate(true); - if ($migrate) { - if (!@copy($this->plConfigPath, $this->userConfigPath)) { - print "Please make sure that Pattern Lab can write a new config to config/.\n"; - exit; - } - } else { - $config = $this->writeNewConfig($config,$defaultConfig); - } - } - - return $config; - - } - - /** - * Use the default config as a base and update it with old config options. Write out a new user config. - * @param {Array} the old configuration file options - * @param {Array} the default configuration file options - * - * @return {Array} the new configuration - */ - protected function writeNewConfig($oldConfig,$defaultConfig) { - - // iterate over the old config and replace values in the new config - foreach ($oldConfig as $key => $value) { - if ($key != "v") { - $defaultConfig[$key] = $value; - } - } - - // create the output data - $configOutput = ""; - foreach ($defaultConfig as $key => $value) { - $configOutput .= $key." = \"".$value."\"\n"; - } - - // write out the new config file - file_put_contents($this->userConfigPath,$configOutput); - - return $defaultConfig; - - } - -} From 2bd8e52a7dda4465cc247869f09140aa0cc1845c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:35:45 -0400 Subject: [PATCH 093/166] moving the documentation parser --- .../Documentation.php} | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) rename core/lib/PatternLab/{DocumentationParser.php => Parsers/Documentation.php} (61%) diff --git a/core/lib/PatternLab/DocumentationParser.php b/core/lib/PatternLab/Parsers/Documentation.php similarity index 61% rename from core/lib/PatternLab/DocumentationParser.php rename to core/lib/PatternLab/Parsers/Documentation.php index 770cb8da3..d832ba431 100644 --- a/core/lib/PatternLab/DocumentationParser.php +++ b/core/lib/PatternLab/Parsers/Documentation.php @@ -1,25 +1,73 @@ Date: Fri, 30 May 2014 22:36:39 -0400 Subject: [PATCH 094/166] moving the template helper and making it static --- core/lib/PatternLab/Template/Helper.php | 56 +++++++++++++++++++++++++ core/lib/PatternLab/TemplateHelper.php | 46 -------------------- 2 files changed, 56 insertions(+), 46 deletions(-) create mode 100644 core/lib/PatternLab/Template/Helper.php delete mode 100644 core/lib/PatternLab/TemplateHelper.php diff --git a/core/lib/PatternLab/Template/Helper.php b/core/lib/PatternLab/Template/Helper.php new file mode 100644 index 000000000..63f7c2712 --- /dev/null +++ b/core/lib/PatternLab/Template/Helper.php @@ -0,0 +1,56 @@ +fileSystem(); + self::$htmlLoader = $templateLoader->vanilla(); + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/TemplateHelper.php b/core/lib/PatternLab/TemplateHelper.php deleted file mode 100644 index ee01e5939..000000000 --- a/core/lib/PatternLab/TemplateHelper.php +++ /dev/null @@ -1,46 +0,0 @@ -patternHead = str_replace("{% pattern-lab-head %}",$htmlHead,$patternHead); - $this->patternFoot = str_replace("{% pattern-lab-foot %}",$extraFoot.$htmlFoot,$patternFoot); - $this->mainPageHead = $this->patternHead; - $this->mainPageFoot = str_replace("{% pattern-lab-foot %}",$htmlFoot,$patternFoot); - - } - -} \ No newline at end of file From 9ac5d7a51046b3158a33a1e30c38d1c3724825c0 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:38:42 -0400 Subject: [PATCH 095/166] moving a filter iterator class --- .../FilterIterator.php} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename core/lib/PatternLab/{SnapshotFilterIterator.php => Snapshot/FilterIterator.php} (73%) diff --git a/core/lib/PatternLab/SnapshotFilterIterator.php b/core/lib/PatternLab/Snapshot/FilterIterator.php similarity index 73% rename from core/lib/PatternLab/SnapshotFilterIterator.php rename to core/lib/PatternLab/Snapshot/FilterIterator.php index 2c6262e4a..16a100acc 100644 --- a/core/lib/PatternLab/SnapshotFilterIterator.php +++ b/core/lib/PatternLab/Snapshot/FilterIterator.php @@ -1,7 +1,7 @@ Date: Fri, 30 May 2014 22:39:15 -0400 Subject: [PATCH 096/166] reorganizing the template loader --- .../{TemplateLoader.php => Template/Loader.php} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename core/lib/PatternLab/{TemplateLoader.php => Template/Loader.php} (76%) diff --git a/core/lib/PatternLab/TemplateLoader.php b/core/lib/PatternLab/Template/Loader.php similarity index 76% rename from core/lib/PatternLab/TemplateLoader.php rename to core/lib/PatternLab/Template/Loader.php index c13109bdc..facc36e82 100644 --- a/core/lib/PatternLab/TemplateLoader.php +++ b/core/lib/PatternLab/Template/Loader.php @@ -1,18 +1,18 @@ new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/"), - "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../templates/partials/") + "loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../../templates/"), + "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../../templates/partials/") )); } From 9a652733a392b72a7d69a2520c7289f1a1855dc5 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:40:04 -0400 Subject: [PATCH 097/166] reorganizing the pattern loaders --- .../Loader.php} | 147 +++++++++--------- .../Loaders/MustacheLoader.php} | 12 +- .../Loaders/TwigLoader.php} | 41 +---- 3 files changed, 90 insertions(+), 110 deletions(-) rename core/lib/PatternLab/{PatternLoader.php => PatternEngine/Loader.php} (89%) rename core/lib/PatternLab/{PatternLoaders/Mustache.php => PatternEngine/Loaders/MustacheLoader.php} (92%) rename core/lib/PatternLab/{PatternLoaders/Twig.php => PatternEngine/Loaders/TwigLoader.php} (83%) diff --git a/core/lib/PatternLab/PatternLoader.php b/core/lib/PatternLab/PatternEngine/Loader.php similarity index 89% rename from core/lib/PatternLab/PatternLoader.php rename to core/lib/PatternLab/PatternEngine/Loader.php index 344b04002..cb2986b66 100644 --- a/core/lib/PatternLab/PatternLoader.php +++ b/core/lib/PatternLab/PatternEngine/Loader.php @@ -1,56 +1,100 @@ patternPaths = $patternPaths; + $this->patternPaths = $options["patternPaths"]; } /** - * Load a new instance that of the Pattern Loader - * - * @return {Object} an instance of the Mustache engine - */ - public function loadPatternLoaderInstance($patternEngine,$sourcePatternsPath) { + * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache + * @param {String} the file contents + * @param {Array} an array of paramters to match + * + * @return {String} the modified file contents + */ + public function findReplaceParameters($fileData, $parameters) { + $numbers = array("zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); + foreach ($parameters as $k => $v) { + if (is_array($v)) { + if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { + if (isset($matches[2])) { + $partialData = ""; + foreach ($v as $v2) { + $partialData .= $this->findReplaceParameters($matches[2], $v2); + } + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); + } + } + } else if ($v == "true") { + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + } else if ($v == "false") { + $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} + $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} + } else if ($k == "listItems") { + $v = ((int)$v != 0) && ((int)$v < 13) ? $numbers[$v] : $v; + if (($v != "zero") && in_array($v,$numbers)) { + $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); + $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); + } + } else { + $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} + $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} + } + } + return $fileData; + } + + /** + * Helper function for getting a Mustache template file name. + * @param {String} the pattern type for the pattern + * @param {String} the pattern sub-type + * + * @return {Array} an array of rendered partials that match the given path + */ + public function getFileName($name,$ext) { - if ($patternEngine == "twig") { - - $loader = new \SplClassLoader('Twig', __DIR__.'/../../lib'); - $loader->setNamespaceSeparator("_"); - $loader->register(); - - $loader = new PatternLoaders\Twig($sourcePatternsPath,array("patternPaths" => $this->patternPaths)); - $instance = new \Twig_Environment($loader); - + $fileName = ""; + $dirSep = DIRECTORY_SEPARATOR; + + // test to see what kind of path was supplied + $posDash = strpos($name,"-"); + $posSlash = strpos($name,$dirSep); + + if (($posSlash === false) && ($posDash !== false)) { + $fileName = $this->getPatternFileName($name); } else { - - $instance = new \Mustache_Engine(array( - "loader" => new PatternLoaders\Mustache($sourcePatternsPath,array("patternPaths" => $this->patternPaths)), - "partials_loader" => new PatternLoaders\Mustache($sourcePatternsPath,array("patternPaths" => $this->patternPaths)) - )); - + $fileName = $name; } - return $instance; + if (substr($fileName, 0 - strlen($ext)) !== $ext) { + $fileName .= $ext; + } + + return $fileName; + } /** @@ -67,12 +111,12 @@ public function getPatternFileName($name) { // see if the pattern is an exact match for patternPaths. if not iterate over patternPaths to find a likely match if (isset($this->patternPaths[$patternType][$pattern])) { - $patternFileName = $this->patternPaths[$patternType][$pattern]["patternSrcPath"]; + $patternFileName = $this->patternPaths[$patternType][$pattern]; } else if (isset($this->patternPaths[$patternType])) { foreach($this->patternPaths[$patternType] as $patternMatchKey=>$patternMatchValue) { $pos = strpos($patternMatchKey,$pattern); if ($pos !== false) { - $patternFileName = $patternMatchValue["patternSrcPath"]; + $patternFileName = $patternMatchValue; break; } } @@ -142,46 +186,6 @@ public function getPartialInfo($partial) { } - /** - * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache - * @param {String} the file contents - * @param {Array} an array of paramters to match - * - * @return {String} the modified file contents - */ - public function findReplaceParameters($fileData, $parameters) { - $numbers = array("zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); - foreach ($parameters as $k => $v) { - if (is_array($v)) { - if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { - if (isset($matches[2])) { - $partialData = ""; - foreach ($v as $v2) { - $partialData .= $this->findReplaceParameters($matches[2], $v2); - } - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); - } - } - } else if ($v == "true") { - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else if ($v == "false") { - $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else if ($k == "listItems") { - $v = ((int)$v != 0) && ((int)$v < 13) ? $numbers[$v] : $v; - if (($v != "zero") && in_array($v,$numbers)) { - $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); - $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); - } - } else { - $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} - $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} - } - } - return $fileData; - } - /** * Helper function to parse the parameters and return them as an array * @param {String} the parameter string @@ -309,4 +313,5 @@ private function parseParameters($string) { } + } diff --git a/core/lib/PatternLab/PatternLoaders/Mustache.php b/core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php similarity index 92% rename from core/lib/PatternLab/PatternLoaders/Mustache.php rename to core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php index 73dedb5b1..4aea2a373 100644 --- a/core/lib/PatternLab/PatternLoaders/Mustache.php +++ b/core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php @@ -1,7 +1,7 @@ patternPaths = $options['patternPaths']; } - $this->patternLoader = new \PatternLab\PatternLoader($this->patternPaths); + $this->patternLoader = new \PatternLab\PatternEngine\Loader($options); } @@ -98,7 +100,7 @@ protected function loadFile($name) { list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); // get the real file path for the pattern - $fileName = $this->getFileName($partialName); + $fileName = $this->baseDir."/".$this->patternLoader->getFileName($partialName,$this->extension); // throw error if path is not found if (!file_exists($fileName)) { diff --git a/core/lib/PatternLab/PatternLoaders/Twig.php b/core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php similarity index 83% rename from core/lib/PatternLab/PatternLoaders/Twig.php rename to core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php index 0a6bce2a8..884e0580f 100644 --- a/core/lib/PatternLab/PatternLoaders/Twig.php +++ b/core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php @@ -1,7 +1,7 @@ setPaths($paths); } $this->patternPaths = $patternPaths['patternPaths']; - $this->patternLoader = new \PatternLab\PatternLoader($this->patternPaths); + $this->patternLoader = new Loader($this->patternPaths); } /** @@ -165,7 +167,7 @@ protected function findTemplate($name) { list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); - $name = $this->getFileName($partialName); + $name = $this->patternLoader->getFileName($partialName,$this->extension); $name = $this->normalizeName($name); @@ -227,33 +229,4 @@ protected function validateName($name) { } - /** - * Helper function for getting a Mustache template file name. - * @param {String} the pattern type for the pattern - * @param {String} the pattern sub-type - * - * @return {Array} an array of rendered partials that match the given path - */ - protected function getFileName($name) { - - $fileName = ""; - $dirSep = DIRECTORY_SEPARATOR; - - // test to see what kind of path was supplied - $posDash = strpos($name,"-"); - $posSlash = strpos($name,$dirSep); - - if (($posSlash === false) && ($posDash !== false)) { - $fileName = $this->patternLoader->getPatternFileName($name); - } else { - $fileName = $name; - } - - if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { - $fileName .= $this->extension; - } - - return $fileName; - - } } From 8ded99df026033b30b34c5487ec0c74dc454feef Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:40:37 -0400 Subject: [PATCH 098/166] removing old pattern info classes --- core/lib/PatternLab/PatternInfo.php | 74 -------- core/lib/PatternLab/PatternInfoRule.php | 70 -------- .../PatternInfoRules/DocumentationRule.php | 90 ---------- .../PatternInfoRules/PatternRule.php | 135 --------------- .../PatternInfoRules/PatternSubtypeRule.php | 62 ------- .../PatternInfoRules/PseudoPatternRule.php | 160 ------------------ 6 files changed, 591 deletions(-) delete mode 100644 core/lib/PatternLab/PatternInfo.php delete mode 100644 core/lib/PatternLab/PatternInfoRule.php delete mode 100644 core/lib/PatternLab/PatternInfoRules/DocumentationRule.php delete mode 100644 core/lib/PatternLab/PatternInfoRules/PatternRule.php delete mode 100644 core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php delete mode 100644 core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php diff --git a/core/lib/PatternLab/PatternInfo.php b/core/lib/PatternLab/PatternInfo.php deleted file mode 100644 index fd7222767..000000000 --- a/core/lib/PatternLab/PatternInfo.php +++ /dev/null @@ -1,74 +0,0 @@ -setFlags(\FilesystemIterator::SKIP_DOTS); - - $patternObjects = iterator_to_array($patternObjects); - ksort($patternObjects); - - self::$d["link"] = array(); - self::$navItems["patternTypes"] = array(); - - foreach ($patternObjects as $name => $object) { - - $ext = $object->getExtension(); - $isDir = $object->isDir(); - $isFile = $object->isFile(); - $path = $object->getPath(); - $pathName = $object->getPathname(); - $name = $object->getFilename(); - - $depth = substr_count(str_replace($options["patternSourceDir"],"",$pathName),DIRECTORY_SEPARATOR); - - foreach (self::$rules as $rule) { - if ($rule->testRule($depth, $ext, $isDir, $isFile, $name)) { - $rule->runRule($depth, $ext, $path, $pathName, $name); - } - } - - } - - } - - public static function loadRules($options) { - foreach (glob(__DIR__."/PatternInfoRules/*.php") as $filename) { - $rule = str_replace(".php","",str_replace(__DIR__."/PatternInfoRules/","",$filename)); - $ruleClass = "PatternLab\PatternInfoRules\\".$rule; - self::$rules[] = new $ruleClass($options); - } - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRule.php b/core/lib/PatternLab/PatternInfoRule.php deleted file mode 100644 index 44e10eb79..000000000 --- a/core/lib/PatternLab/PatternInfoRule.php +++ /dev/null @@ -1,70 +0,0 @@ -patternSourceDir = $options["patternSourceDir"]; - - } - - public function testRule($depth, $ext, $isDir, $isFile, $name) { - - if (($this->depthProp != 3) && ($depth != $this->depthProp)) { - return false; - } - - if (($ext == $this->extProp) && ($isDir == $this->isDirProp) && ($isFile == $this->isFileProp)) { - if ($this->searchProp != "") { - return (strpos($name,$this->searchProp) !== false) ? true : false; - } else { - return true; - } - } - - return false; - - } - - /** - * Get the name for a given pattern sans any possible digits used for reordering - * @param {String} the pattern based on the filesystem name - * @param {Boolean} whether or not to strip slashes from the pattern name - * - * @return {String} a lower-cased version of the pattern name - */ - protected function getPatternName($pattern, $clean = true) { - $patternBits = explode("-",$pattern,2); - $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; - return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; - } - - protected function findKey($haystack,$needle,$attribute) { - $key = false; - foreach ($haystack as $strawKey => $strawValues) { - if ($strawValues[$attribute] == $needle) { - $key = $strawKey; - break; - } - } - return $key; - } -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php b/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php deleted file mode 100644 index 68e89d585..000000000 --- a/core/lib/PatternLab/PatternInfoRules/DocumentationRule.php +++ /dev/null @@ -1,90 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "md"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ""; - - } - - public function runRule($depth, $ext, $path, $pathName, $name) { - - $bi = PatternInfo::$bi; - $ni = PatternInfo::$ni; - $patternSubtype = PatternInfo::$patternSubtype; - $patternTypeDash = PatternInfo::$patternTypeDash; - - $patternFull = $name; // 00-colors.md - $pattern = str_replace(".".$this->extProp,"",$patternFull); // 00-colors - - // make sure the pattern isn't hidden - if ($patternFull[0] != "_") { - - // parse data - $text = file_get_contents($pathName); - list($yaml,$markdown) = DocumentationParser::parse($text); - - if ($depth == 1) { - - // add to pattern subtype - if (isset($yaml["title"])) { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeLC"] = strtolower($yaml["title"]); - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeUC"] = ucwords($yaml["title"]); - unset($yaml["title"]); - } - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeDesc"] = $markdown; - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeMeta"] = $yaml; - - } else if ($depth == 2) { - - // get base info - $patternDash = $this->getPatternName($pattern,false); // colors - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - // see if the pattern is already part of the nav - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); - if ($key === false) { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = array(); - $a = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"]; - end($a); - $key = key($a); - } - - // add to the pattern - if (isset($yaml["title"])) { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"] = ucwords($yaml["title"]); - unset($yaml["title"]); - } - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternDesc"] = $markdown; - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternMeta"] = $yaml; - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternPartial"] = $patternPartial; - - } - - } - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternRule.php b/core/lib/PatternLab/PatternInfoRules/PatternRule.php deleted file mode 100644 index d4598b7ef..000000000 --- a/core/lib/PatternLab/PatternInfoRules/PatternRule.php +++ /dev/null @@ -1,135 +0,0 @@ -patternExtension = $options["patternExtension"]; - - $this->depthProp = 3; // 3 means that depth won't be checked - $this->extProp = $this->patternExtension; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ""; - - } - - public function runRule($depth, $ext, $path, $pathName, $name) { - - $bi = PatternInfo::$bi; - $ni = PatternInfo::$ni; - $patternSubtype = PatternInfo::$patternSubtype; - $patternSubtypeDash = PatternInfo::$patternSubtypeDash; - $patternSubtypeSet = PatternInfo::$patternSubtypeSet; - $patternType = PatternInfo::$patternType; - $patternTypeDash = PatternInfo::$patternTypeDash; - $dirSep = DIRECTORY_SEPARATOR; - - $patternFull = $name; // 00-colors.mustache - $pattern = str_replace(".".$this->patternExtension,"",$patternFull); // 00-colors - - // check for pattern state - $patternState = ""; - if (strpos($pattern,"@") !== false) { - $patternBits = explode("@",$pattern,2); - $pattern = $patternBits[0]; - $patternState = $patternBits[1]; - } - - if ($patternSubtypeSet) { - $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - } else { - $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) - } - - // track to see if this pattern should get rendered - $render = false; - - // make sure the pattern isn't hidden - if ($patternFull[0] != "_") { - - // set-up the names - $patternDash = $this->getPatternName($pattern,false); // colors - $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - // see if the pattern name is already set via .md file - $patternName = ucwords($patternClean); - if ($depth == 2) { - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); - if ($key !== false) { - $patternName = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"]; - } - } - - // set-up the info for the nav - $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", - "patternSrcPath" => str_replace($this->patternSourceDir,"",$pathName), - "patternName" => $patternName, - "patternState" => $patternState, - "patternPartial" => $patternPartial); - - // add to the nav - if ($depth == 1) { - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"],$patternPartial,"patternPartial"); - if ($key !== false) { - PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key], $patternInfo); - } else { - PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; - } - } else { - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); - if ($key !== false) { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key], $patternInfo); - } else { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; - } - } - - // add to the link var for inclusion in patterns - PatternInfo::$d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; - - // yup, this pattern should get rendered - $render = true; - - } else { - - // replace the underscore to generate a good file pattern name - $patternDash = $this->getPatternName(str_replace("_","",$pattern),false); // colors - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - - } - - // add all patterns to patternPaths - $patternSrcPath = str_replace($this->patternSourceDir,"",str_replace(".".$this->patternExtension,"",$pathName)); - $patternDestPath = $patternPathDash; - PatternInfo::$patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, - "patternDestPath" => $patternDestPath, - "patternPartial" => $patternPartial, - "patternState" => $patternState, - "patternType" => $patternTypeDash, - "render" => $render); - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php b/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php deleted file mode 100644 index c66ee8ebd..000000000 --- a/core/lib/PatternLab/PatternInfoRules/PatternSubtypeRule.php +++ /dev/null @@ -1,62 +0,0 @@ -depthProp = 1; - $this->extProp = ""; - $this->isDirProp = true; - $this->isFileProp = false; - $this->searchProp = ""; - - } - - public function runRule($depth, $ext, $path, $pathName, $name) { - - // is this the first bucket to be set? - PatternInfo::$ni = (!PatternInfo::$patternSubtypeSet) ? 0 : PatternInfo::$ni + 1; - $ni = PatternInfo::$ni; - $bi = PatternInfo::$bi; - $patternTypeDash = PatternInfo::$patternTypeDash; - - // set-up the names - $patternSubtype = $name; // 02-blocks - $patternSubtypeDash = $this->getPatternName($name,false); // blocks - $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) - - // add to patternPartials - PatternInfo::$patternPartials[$patternTypeDash."-".$patternSubtypeDash] = array(); - - // add a new patternSubtype to the nav - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternSubtypeClean), - "patternSubtypeUC" => ucwords($patternSubtypeClean), - "patternSubtype" => $patternSubtype, - "patternSubtypeDash" => $patternSubtypeDash, - "patternSubtypeItems" => array()); - - // starting a new set of pattern types. it might not have any pattern subtypes - PatternInfo::$patternSubtype = $patternSubtype; - PatternInfo::$patternSubtypeDash = $patternSubtypeDash; - PatternInfo::$patternSubtypeSet = true; - - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php b/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php deleted file mode 100644 index 5335d0f2e..000000000 --- a/core/lib/PatternLab/PatternInfoRules/PseudoPatternRule.php +++ /dev/null @@ -1,160 +0,0 @@ -patternExtension = $options["patternExtension"]; - - $this->depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "json"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = "~"; - - } - - public function runRule($depth, $ext, $path, $pathName, $name) { - - $bi = PatternInfo::$bi; - $ni = PatternInfo::$ni; - $patternSubtype = PatternInfo::$patternSubtype; - $patternSubtypeDash = PatternInfo::$patternSubtypeDash; - $patternSubtypeSet = PatternInfo::$patternSubtypeSet; - $patternType = PatternInfo::$patternType; - $patternTypeDash = PatternInfo::$patternTypeDash; - $dirSep = DIRECTORY_SEPARATOR; - - $patternSubtypeInclude = ($patternSubtypeSet) ? $patternSubtype."-" : ""; - $patternFull = $name; - - if ($patternFull[0] != "_") { - - // check for a pattern state - $patternState = ""; - $patternBits = explode("@",$patternFull,2); - if (isset($patternBits[1])) { - $patternState = str_replace(".json","",$patternBits[1]); - $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); - } - - // set-up the names - // $patternFull is defined above 00-colors.mustache - $patternBits = explode("~",$patternFull); - $patternBase = $patternBits[0].".".$this->patternExtension; // 00-homepage.mustache - $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage - $patternBaseJSON = $patternBits[0].".json"; // 00-homepage.json - $stripJSON = str_replace(".json","",$patternBits[1]); - $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); - $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency - $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency - $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency - $patternClean = str_replace("-"," ",$patternDash); // homepage emergency - $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency - - // add to patternPaths - if ($patternSubtypeSet) { - $patternPath = $patternType.$dirSep.$patternSubtype.$dirSep.$pattern; // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - } else { - $patternPath = $patternType.$dirSep.$pattern; // 00-atoms/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-00-colors (file path) - } - - // add all patterns to patternPaths - $patternSrcPath = PatternInfo::$patternPaths[$patternTypeDash][$patternBaseDash]["patternSrcPath"]; - $patternDestPath = $patternPathDash; - PatternInfo::$patternPaths[$patternTypeDash][$patternDash] = array("patternSrcPath" => $patternSrcPath, - "patternDestPath" => $patternDestPath, - "patternPartial" => $patternPartial, - "patternState" => $patternState, - "patternType" => $patternTypeDash, - "render" => true); - - // see if the pattern name is already set via .md file - $patternName = ucwords($patternClean); - if ($depth == 2) { - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial"); - if ($key !== false) { - $patternName = PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key]["patternName"]; - } - } - - // set-up the info for the nav - $patternInfo = array("patternPath" => $patternPathDash."/".$patternPathDash.".html", - "patternSrcPath" => str_replace($this->patternSourceDir,"",preg_replace("/\~(.*)\.json/",".".$this->patternExtension,$pathName)), - "patternName" => $patternName, - "patternState" => $patternState, - "patternPartial" => $patternPartial); - - // add to the nav - if ($depth == 1) { - $key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"],$patternPartial,"patternPartial"); - if ($key !== false) { - PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][$key], $patternInfo); - } else { - PatternInfo::$navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; - } - } else { - if ($key = $this->findKey(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"],$patternPartial,"patternPartial")) { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = array_merge(PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key], $patternInfo); - } else { - PatternInfo::$navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][$key] = $patternInfo; - } - } - - // add to the link var for inclusion in patterns - PatternInfo::$d["link"][$patternPartial] = "../../patterns/".$patternPathDash."/".$patternPathDash.".html"; - - // get the base data - $patternDataBase = array(); - if (file_exists($path."/".$patternBaseJSON)) { - $data = file_get_contents($path."/".$patternBaseJSON); - $patternDataBase = json_decode($data,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); - } - } - - // get the special pattern data - $data = file_get_contents($pathName); - $patternData = (array) json_decode($data); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($name,$jsonErrorMessage,$data); - } - - // merge them for the file - if (!isset(PatternInfo::$d["patternSpecific"][$patternPartial])) { - PatternInfo::$d["patternSpecific"][$patternPartial] = array(); - PatternInfo::$d["patternSpecific"][$patternPartial]["data"] = array(); - PatternInfo::$d["patternSpecific"][$patternPartial]["listItems"] = array(); - } - - if (is_array($patternDataBase) && is_array($patternData)) { - PatternInfo::$d["patternSpecific"][$patternPartial]["data"] = array_merge($patternDataBase, $patternData); - } - - } - - } - -} - From 7afe8aa5737f3f6f6a497ef71497903a791745f6 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:41:08 -0400 Subject: [PATCH 099/166] adding data class to handle storing global data --- core/lib/PatternLab/Data.php | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 core/lib/PatternLab/Data.php diff --git a/core/lib/PatternLab/Data.php b/core/lib/PatternLab/Data.php new file mode 100644 index 000000000..0676bb3f4 --- /dev/null +++ b/core/lib/PatternLab/Data.php @@ -0,0 +1,204 @@ +d + $d = self::copy(); + + if (isset($d["patternSpecific"]) && array_key_exists($patternPartial,$d["patternSpecific"])) { + + if (!empty($d["patternSpecific"][$patternPartial]["data"])) { + $d = array_replace_recursive($d, $d["patternSpecific"][$patternPartial]["data"]); + } + + if (!empty($d["patternSpecific"][$patternPartial]["listItems"])) { + + $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); + + $k = 0; + $c = count($d["patternSpecific"][$patternPartial]["listItems"]); + + while ($k < $c) { + $section = $numbers[$k]; + $d["listItems"][$section] = array_replace_recursive( $d["listItems"][$section], $d["patternSpecific"][$patternPartial]["listItems"][$section]); + $k++; + } + + } + + } + + if (!empty($extraData)) { + $d = array_replace_recursive($d, $extraData); + } + + unset($d["patternSpecific"]); + + return $d; + + } + + /** + * Print out the data var. For debugging purposes + * + * @return {String} the formatted version of the d object + */ + public static function printData() { + print_r(self::$store); + } + +} \ No newline at end of file From be566ac9f5441f9e328eba2bccc74ff21abcf60e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:41:34 -0400 Subject: [PATCH 100/166] adding file util class to handle select functions across PL --- core/lib/PatternLab/FileUtil.php | 62 ++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 core/lib/PatternLab/FileUtil.php diff --git a/core/lib/PatternLab/FileUtil.php b/core/lib/PatternLab/FileUtil.php new file mode 100644 index 000000000..ead066bd3 --- /dev/null +++ b/core/lib/PatternLab/FileUtil.php @@ -0,0 +1,62 @@ + Date: Fri, 30 May 2014 22:42:29 -0400 Subject: [PATCH 101/166] adding pattern data class to handle central storage of pattern data --- core/lib/PatternLab/PatternData.php | 118 ++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 core/lib/PatternLab/PatternData.php diff --git a/core/lib/PatternLab/PatternData.php b/core/lib/PatternLab/PatternData.php new file mode 100644 index 000000000..b22642246 --- /dev/null +++ b/core/lib/PatternLab/PatternData.php @@ -0,0 +1,118 @@ +setFlags(\FilesystemIterator::SKIP_DOTS); + + $patternObjects = iterator_to_array($patternObjects); + ksort($patternObjects); + + foreach ($patternObjects as $name => $object) { + + $ext = $object->getExtension(); + $isDir = $object->isDir(); + $isFile = $object->isFile(); + + $path = str_replace(__DIR__.Config::$options["patternSourceDir"],"",$object->getPath()); + $pathName = str_replace(__DIR__.Config::$options["patternSourceDir"],"",$object->getPathname()); + $name = $object->getFilename(); + $depth = substr_count($pathName,DIRECTORY_SEPARATOR); + + // iterate over the rules and see if the current file matches one, if so run the rule + foreach (self::$rules as $rule) { + if ($rule->test($depth, $ext, $isDir, $isFile, $name)) { + $rule->run($depth, $ext, $path, $pathName, $name); + } + } + + } + + // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns + $dataLinkExporter = new DataLinkExporter(); + $dataLinkExporter->run(); + + // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns + $dataMergeExporter = new DataMergeExporter(); + $dataMergeExporter->run(); + + // add the lineage info to PatternData::$store + $lineageHelper = new LineageHelper(); + $lineageHelper->run(); + + // using the lineage info update the pattern states on PatternData::$store + $patternStateHelper = new PatternStateHelper(); + $patternStateHelper->run(); + + // set-up code pattern paths + $ppdExporter = new PatternPathSrcExporter(); + $patternPathSrc = $ppdExporter->run(); + $options = array(); + $options["patternPaths"] = $patternPathSrc; + + // render out all of the patterns and store the generated info in PatternData::$store + $patternCodeHelper = new PatternCodeHelper($options); + $patternCodeHelper->run(); + + // loop through and check KSS (this will change in the future) + $KSSHelper = new KSSHelperPlugin($options); + $KSSHelper->run(); + + } + + /** + * Load all of the rules related to Pattern Data + */ + public static function loadRules($options) { + foreach (glob(__DIR__."/PatternData/Rules/*.php") as $filename) { + $rule = str_replace(".php","",str_replace(__DIR__."/PatternData/Rules/","",$filename)); + $ruleClass = "\PatternLab\PatternData\Rules\\".$rule; + self::$rules[] = new $ruleClass($options); + } + } + +} \ No newline at end of file From 56a9bab4d8dab814a43a784bd4c236791cdf2ea1 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:42:53 -0400 Subject: [PATCH 102/166] pattern data rules to handle ingesting pattern data --- core/lib/PatternLab/PatternData/Rule.php | 108 +++++++++++++ .../PatternData/Rules/DocumentationRule.php | 94 ++++++++++++ .../Rules/PatternInfoListItemsRule.php | 68 +++++++++ .../PatternData/Rules/PatternInfoRule.php | 77 ++++++++++ .../PatternData/Rules/PatternRule.php | 100 ++++++++++++ .../PatternData/Rules/PatternSubtypeRule.php | 77 ++++++++++ .../PatternData/Rules/PseudoPatternRule.php | 143 ++++++++++++++++++ 7 files changed, 667 insertions(+) create mode 100644 core/lib/PatternLab/PatternData/Rule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/DocumentationRule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/PatternRule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php create mode 100644 core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php diff --git a/core/lib/PatternLab/PatternData/Rule.php b/core/lib/PatternLab/PatternData/Rule.php new file mode 100644 index 000000000..bfd39b0a5 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rule.php @@ -0,0 +1,108 @@ +depthProp != 3) && ($depth != $this->depthProp)) { + return false; + } + + if (($this->compareProp($ext,$this->extProp)) && ($isDir == $this->isDirProp) && ($isFile == $this->isFileProp)) { + $result = true; + if ($this->searchProp != "") { + $result = $this->compareProp($name,$this->searchProp); + } + if ($this->ignoreProp != "") { + $result = ($this->compareProp($name,$this->ignoreProp)) ? false : true; + } + return $result; + } + + return false; + + } + + /** + * Compare the search and ignore props against the name. + * Can use && or || in the comparison + * @param {String} the name of the item + * @param {String} the value of the property to compare + * + * @return {Boolean} whether the compare was successful or not + */ + protected function compareProp($name,$propCompare) { + + if (($name == "") && ($propCompare == "")) { + $result = true; + } else if ((($name == "") && ($propCompare != "")) || (($name != "") && ($propCompare == ""))) { + $result = false; + } else if (strpos($propCompare,"&&") !== false) { + $result = true; + $props = explode("&&",$propCompare); + foreach ($props as $prop) { + $pos = (strpos($name,$prop) !== false) ? true : false; + $result = ($result && $pos); + } + } else if (strpos($propCompare,"||") !== false) { + $result = false; + $props = explode("||",$propCompare); + foreach ($props as $prop) { + $pos = (strpos($name,$prop) !== false) ? true : false; + $result = ($result || $pos); + } + } else { + $result = (strpos($name,$propCompare) !== false) ? true : false; + } + + return $result; + + } + + /** + * Get the name for a given pattern sans any possible digits used for reordering + * @param {String} the pattern based on the filesystem name + * @param {Boolean} whether or not to strip slashes from the pattern name + * + * @return {String} a lower-cased version of the pattern name + */ + protected function getPatternName($pattern, $clean = true) { + $patternBits = explode("-",$pattern,2); + $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; + return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php b/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php new file mode 100644 index 000000000..43feba001 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php @@ -0,0 +1,94 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "md"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ""; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternType = PatternData::$patternType; + $patternTypeDash = PatternData::$patternTypeDash; + $dirSep = PatternData::$dirSep; + + // set-up the names + $docFull = $name; // 00-colors.md + $doc = str_replace(".".$this->extProp,"",$docFull); // 00-colors + $docDash = $this->getPatternName(str_replace("_","",$doc),false); // colors + $docPartial = $patternTypeDash."-".$docDash; + + // make sure the pattern isn't hidden + $hidden = ($docFull[0] == "_"); + + // if the pattern isn't hidden do stuff + if (!$hidden) { + + // parse data + $text = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); + list($yaml,$markdown) = Documentation::parse($text); + + // grab the title and unset it from the yaml so it doesn't get duped in the meta + if (isset($yaml["title"])) { + $title = $yaml["title"]; + unset($yaml["title"]); + } + + // figure out if this is a pattern subtype + $patternSubtypeDoc = false; + if ($depth == 1) { + // go through all of the directories to see if this one matches our doc + foreach (glob(__DIR__."/../..".Config::$options["patternSourceDir"].$patternType."/*",GLOB_ONLYDIR) as $dir) { + $dir = str_replace(__DIR__."/../..".Config::$options["patternSourceDir"].$patternType."/","",$dir); + if ($dir == $doc) { + $patternSubtypeDoc = true; + break; + } + } + + } + + $category = ($patternSubtypeDoc) ? "patternSubtype" : "pattern"; + $patternStoreKey = ($patternSubtypeDoc) ? $docPartial."-plsubtype" : $docPartial; + + $patternStoreData = array("category" => $category, + "nameClean" => $title, + "desc" => $markdown, + "descExists" => true, + "meta" => $yaml, + "full" => $doc); + + // if the pattern data store already exists make sure this data overwrites it + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; + + } + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php b/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php new file mode 100644 index 000000000..631324605 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php @@ -0,0 +1,68 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "json||yaml"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ".listitems."; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternTypeDash = PatternData::$patternTypeDash; + + // set-up the names + $patternFull = $name; // foo.listitems.json + $pattern = str_replace(".listitems.".$ext,"",$patternFull); // foo + $patternDash = $this->getPatternName($pattern,false); // foo + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo + + // should this pattern get rendered? + $hidden = ($patternFull[0] == "_"); + + if (!$hidden) { + + $patternStoreData = array("category" => "pattern"); + + $data = Data::getListItems($pathName,$ext); + $patternStoreData["listItems"] = $data; + + } + + // create a key for the data store + $patternStoreKey = $patternPartial; + + // if the pattern data store already exists make sure it is merged and overwrites this data + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; + + } + +} + diff --git a/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php b/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php new file mode 100644 index 000000000..aec70b2d7 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php @@ -0,0 +1,77 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "json||yaml"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ""; + $this->ignoreProp = "~"; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternTypeDash = PatternData::$patternTypeDash; + + // set-up the names + $patternFull = $name; // foo.json + $pattern = str_replace(".".$ext,"",$patternFull); // foo + $patternDash = $this->getPatternName($pattern,false); // foo + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo + + // should this pattern get rendered? + $hidden = ($patternFull[0] == "_"); + + if (!$hidden) { + + $patternStoreData = array("category" => "pattern"); + + $file = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); + + if ($ext == "json") { + $data = json_decode($file,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($name,$jsonErrorMessage,$data); + } + } else { + $data = Yaml::parse($file); + } + + $patternStoreData["data"] = $data; + + // create a key for the data store + $patternStoreKey = $patternPartial; + + // if the pattern data store already exists make sure it is merged and overwrites this data + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; + + } + + } + +} + diff --git a/core/lib/PatternLab/PatternData/Rules/PatternRule.php b/core/lib/PatternLab/PatternData/Rules/PatternRule.php new file mode 100644 index 000000000..e225ff199 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PatternRule.php @@ -0,0 +1,100 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = Config::$options["patternExtension"]; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = ""; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternSubtype = PatternData::$patternSubtype; + $patternSubtypeClean = PatternData::$patternSubtypeClean; + $patternSubtypeDash = PatternData::$patternSubtypeDash; + $patternType = PatternData::$patternType; + $patternTypeClean = PatternData::$patternTypeClean; + $patternTypeDash = PatternData::$patternTypeDash; + $dirSep = PatternData::$dirSep; + + // set-up the names + $patternFull = $name; // 00-colors.mustache + $pattern = str_replace(".".Config::$options["patternExtension"],"",$patternFull); // 00-colors + $patternState = ""; + + // check for pattern state + if (strpos($pattern,"@") !== false) { + $patternBits = explode("@",$pattern,2); + $pattern = $patternBits[0]; + $patternState = $patternBits[1]; + } + + // finish setting up vars + $patternDash = $this->getPatternName(str_replace("_","",$pattern),false); // colors + $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) + $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors + $patternPath = str_replace(".".Config::$options["patternExtension"],"",$pathName); // 00-atoms/01-global/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) + + // should this pattern get rendered? + $hidden = ($patternFull[0] == "_"); + + // create a key for the data store + $patternStoreKey = $patternPartial; + + // collect the data + $patternStoreData = array("category" => "pattern", + "name" => $pattern, + "partial" => $patternPartial, + "nameDash" => $patternDash, + "nameClean" => $patternClean, + "type" => $patternType, + "typeDash" => $patternTypeDash, + "breadcrumb" => $patternTypeClean, + "state" => $patternState, + "hidden" => $hidden, + "depth" => $depth, + "ext" => $ext, + "path" => $path, + "pathName" => $patternPath, + "pathDash" => $patternPathDash, + "isDir" => $this->isDirProp, + "isFile" => $this->isFileProp); + + // add any subtype info if necessary + if ($depth == 2) { + $patternStoreData["subtype"] = $patternSubtype; + $patternStoreData["subtypeDash"] = $patternSubtypeDash; + $patternStoreData["breadcrumb"] = $patternTypeClean." > ".$patternSubtypeClean; + } + + // if the pattern data store already exists make sure it is merged and overwrites this data + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php b/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php new file mode 100644 index 000000000..5ae93614f --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php @@ -0,0 +1,77 @@ +depthProp = 1; + $this->extProp = ""; + $this->isDirProp = true; + $this->isFileProp = false; + $this->searchProp = ""; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternType = PatternData::$patternType; + $patternTypeDash = PatternData::$patternTypeDash; + $patternTypeClean = PatternData::$patternTypeClean; + $dirSep = PatternData::$dirSep; + + // set-up the names + $patternSubtype = $name; // 02-blocks + $patternSubtypeDash = $this->getPatternName($name,false); // blocks + $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) + $patternSubtypePath = $pathName; // 00-atoms/02-blocks + $patternSubtypePathDash = str_replace($dirSep,"-",$patternSubtypePath); // 00-atoms-02-blocks (file path) + + // create a key for the data store + $patternStoreKey = $patternTypeDash."-".$patternSubtypeDash."-plsubtype"; + + // collect the data + $patternStoreData = array("category" => "patternSubtype", + "name" => $patternSubtype, + "nameDash" => $patternSubtypeDash, + "nameClean" => $patternSubtypeClean, + "type" => $patternType, + "typeDash" => $patternTypeDash, + "breadcrumb" => $patternTypeClean, + "depth" => $depth, + "ext" => $ext, + "path" => $path, + "pathName" => $patternSubtypePath, + "pathDash" => $patternSubtypePathDash, + "isDir" => $this->isDirProp, + "isFile" => $this->isFileProp); + + // if the pattern data store already exists make sure it is merged and overwrites this data + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; + + // starting a new set of pattern types. it might not have any pattern subtypes + PatternData::$patternSubtype = $patternSubtype; + PatternData::$patternSubtypeClean = $patternSubtypeClean; + PatternData::$patternSubtypeDash = $patternSubtypeDash; + PatternData::$patternSubtypeSet = true; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php b/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php new file mode 100644 index 000000000..73ab58b91 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php @@ -0,0 +1,143 @@ +depthProp = 3; // 3 means that depth won't be checked + $this->extProp = "json||yaml"; + $this->isDirProp = false; + $this->isFileProp = true; + $this->searchProp = "~"; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // load default vars + $patternSubtype = PatternData::$patternSubtype; + $patternSubtypeDash = PatternData::$patternSubtypeDash; + $patternType = PatternData::$patternType; + $patternTypeDash = PatternData::$patternTypeDash; + $dirSep = PatternData::$dirSep; + + // set-up the names + $patternFull = $name; // 00-colors.mustache + $patternState = ""; + + // check for pattern state + if (strpos($patternFull,"@") !== false) { + $patternBits = explode("@",$patternFull,2); + $patternState = str_replace(".".$ext,"",$patternBits[1]); + $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); + } + + // finish setting up vars + $patternBits = explode("~",$patternFull); + $patternBase = $patternBits[0].".".Config::$options["patternExtension"]; // 00-homepage.mustache + $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage + $patternBaseOrig = $patternTypeDash."-".$patternBaseDash; // pages-homepage + $patternBaseData = $patternBits[0].".".$ext; // 00-homepage.json + $stripJSON = str_replace(".".$ext,"",$patternBits[1]); + $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); + $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency + $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency + $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency + $patternClean = str_replace("-"," ",$patternDash); // homepage emergency + $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency + $patternPath = str_replace(".".$ext,"",str_replace("~","-",$pathName)); // 00-atoms/01-global/00-colors + $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) + $patternPathOrigBits = explode("~",$pathName); + $patternPathOrig = $patternPathOrigBits[0]; // 04-pages/00-homepage + $patternPathOrigDash = str_replace($dirSep,"-",$patternPathOrig); // 04-pages-00-homepage + + // should this pattern get rendered? + $hidden = ($patternFull[0] == "_"); + + // create a key for the data store + $patternStoreKey = $patternPartial; + + // collect the data + $patternStoreData = array("category" => "pattern", + "name" => $pattern, + "partial" => $patternPartial, + "nameDash" => $patternDash, + "nameClean" => $patternClean, + "type" => $patternType, + "typeDash" => $patternTypeDash, + "breadcrumb" => $patternType, + "state" => $patternState, + "hidden" => $hidden, + "depth" => $depth, + "ext" => $ext, + "path" => $path, + "pathName" => $patternPath, + "pathDash" => $patternPathDash, + "isDir" => $this->isDirProp, + "isFile" => $this->isFileProp, + "pseudo" => true, + "original" => $patternBaseOrig, + "pathOrig" => $patternPathOrig, + "pathOrigDash" => $patternPathOrigDash); + + // add any subtype info if necessary + if ($depth == 2) { + $patternStoreData["subtype"] = $patternSubtype; + $patternStoreData["subtypeDash"] = $patternSubtypeDash; + $patternStoreData["breadcrumb"] = $patternType." > ".$patternSubtype; + } + + $patternDataBase = array(); + if (file_exists(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$path."/".$patternBaseData)) { + $data = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$path."/".$patternBaseData); + if ($ext == "json") { + $patternDataBase = json_decode($data,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); + } + } else { + $patternDataBase = Yaml::parse($data); + } + + } + + // get the data for the pseudo-pattern + $data = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); + if ($ext == "json") { + $patternData = json_decode($data,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($name,$jsonErrorMessage,$data); + } + } else { + $patternData = Yaml::parse($data); + } + + $patternStoreData["data"] = array_replace_recursive($patternDataBase, $patternData); + + // if the pattern data store already exists make sure it is merged and overwrites this data + PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; + + } + +} + From 0a45356c669e370be908d3c6475222ad720a763d Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:43:15 -0400 Subject: [PATCH 103/166] pattern data helpers to handle modifying pattern data --- core/lib/PatternLab/PatternData/Helper.php | 21 +++ .../PatternData/Helpers/LineageHelper.php | 165 ++++++++++++++++++ .../PatternData/Helpers/PatternCodeHelper.php | 72 ++++++++ .../Helpers/PatternStateHelper.php | 123 +++++++++++++ 4 files changed, 381 insertions(+) create mode 100644 core/lib/PatternLab/PatternData/Helper.php create mode 100644 core/lib/PatternLab/PatternData/Helpers/LineageHelper.php create mode 100644 core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php create mode 100644 core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php diff --git a/core/lib/PatternLab/PatternData/Helper.php b/core/lib/PatternLab/PatternData/Helper.php new file mode 100644 index 000000000..558624e9b --- /dev/null +++ b/core/lib/PatternLab/PatternData/Helper.php @@ -0,0 +1,21 @@ + $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"]))) { + + $patternLineages = array(); + $fileName = $patternStoreData["pathName"].".".Config::$options["patternExtension"]; + $fileNameFull = __DIR__."/../..".Config::$options["patternSourceDir"].$fileName; + + if (file_exists($fileNameFull)) { + $foundLineages = $this->findLineages($fileNameFull); + } + + if (!empty($foundLineages)) { + + foreach ($foundLineages as $lineage) { + + if (isset(PatternData::$store[$lineage])) { + + $patternLineages[] = array("lineagePattern" => $lineage, + "lineagePath" => "../../patterns/".$patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"); + + } else { + + if (strpos($lineage, '/') === false) { + print "You may have a typo in ".$fileName.". {{> ".$lineage." }} is not a valid pattern.\n"; + } + + } + + } + + // add the lineages to the PatternData::$store + PatternData::$store[$patternStoreKey]["lineages"] = $patternLineages; + + } + + } + + } + + // handle all of those pseudo patterns + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { + + // add the lineages to the PatternData::$store + $patternStoreKeyOriginal = $patternStoreData["original"]; + PatternData::$store[$patternStoreKey]["lineages"] = PatternData::$store[$patternStoreKeyOriginal]["lineages"]; + + } + + } + + // check for the reverse lineages and skip pseudo patterns + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"]))) { + + $patternLineagesR = array(); + + foreach (PatternData::$store as $haystackKey => $haystackData) { + + if (($haystackData["category"] == "pattern") && (isset($haystackData["lineages"]))) { + + foreach ($haystackData["lineages"] as $haystackLineage) { + + if ($haystackLineage["lineagePattern"] == $patternStoreData["partial"]) { + + $foundAlready = false; + foreach ($patternLineagesR as $patternCheck) { + + if ($patternCheck["lineagePattern"] == $patternStoreData["partial"]) { + $foundAlready = true; + break; + } + + } + + if (!$foundAlready) { + + if (isset(PatternData::$store[$haystackKey])) { + + $path = PatternData::$store[$haystackKey]["pathDash"]; + $patternLineagesR[] = array("lineagePattern" => $haystackKey, + "lineagePath" => "../../patterns/".$path."/".$path.".html"); + + } + + } + + } + + } + + } + + } + + PatternData::$store[$patternStoreKey]["lineagesR"] = $patternLineagesR; + + } + + } + + // handle all of those pseudo patterns + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { + + // add the lineages to the PatternData::$store + $patternStoreKeyOriginal = $patternStoreData["original"]; + PatternData::$store[$patternStoreKey]["lineagesR"] = PatternData::$store[$patternStoreKeyOriginal]["lineagesR"]; + + } + + } + + } + + + /** + * Get the lineage for a given pattern by parsing it and matching mustache partials + * @param {String} the filename for the pattern to be parsed + * + * @return {Array} a list of patterns + */ + protected function findLineages($filename) { + $data = file_get_contents($filename); + if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-_]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)?}}/',$data,$matches)) { + return array_unique($matches[2]); + } + return array(); + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php b/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php new file mode 100644 index 000000000..f78ebbd04 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php @@ -0,0 +1,72 @@ +patternPaths = $options["patternPaths"]; + + } + + public function run() { + + $options = array(); + $options["patternPaths"] = $this->patternPaths; + PatternEngine::setup($options); + + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { + + $patternFooterData = array("patternFooterData" => array()); + //$patternFooterData["patternFooterData"]["cssEnabled"] = (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) ? "true" : "false"; + $patternFooterData["patternFooterData"]["lineage"] = isset($patternStoreData["lineages"]) ? json_encode($patternStoreData["lineages"]) : ""; + $patternFooterData["patternFooterData"]["lineageR"] = isset($patternStoreData["lineagesR"]) ? json_encode($patternStoreData["lineagesR"]) : ""; + $patternFooterData["patternFooterData"]["patternBreadcrumb"] = $patternStoreData["breadcrumb"]; + $patternFooterData["patternFooterData"]["patternDesc"] = (isset($patternStoreData["desc"])) ? $patternStoreData["desc"] : ""; + $patternFooterData["patternFooterData"]["patternExtension"] = Config::$options["patternExtension"]; + $patternFooterData["patternFooterData"]["patternModifiers"] = (isset($patternStoreData["modifiers"])) ? json_encode($patternStoreData["modifiers"]) : "[]"; + $patternFooterData["patternFooterData"]["patternName"] = $patternStoreData["nameClean"]; + $patternFooterData["patternFooterData"]["patternPartial"] = $patternStoreData["partial"]; + $patternFooterData["patternFooterData"]["patternState"] = $patternStoreData["state"]; + + $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; + + $data = Data::getPatternSpecificData($patternStoreKey,$patternFooterData); + + $header = Render::Header(Helper::$patternHead,$data); + $code = Render::Pattern($srcPath,$data); + $footer = Render::Footer(Helper::$patternFoot,$data); + + PatternData::$store[$patternStoreKey]["header"] = $header; + PatternData::$store[$patternStoreKey]["code"] = $code; + PatternData::$store[$patternStoreKey]["footer"] = $footer; + + } + + } + + } + +} diff --git a/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php b/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php new file mode 100644 index 000000000..aa82d1479 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php @@ -0,0 +1,123 @@ + $patternStoreData) { + + if ($patternStoreData["category"] == "pattern") { + + $patternState = $patternStoreData["state"]; + + // make sure the pattern has a given state + if ($patternState != "") { + + $patternStateDigit = array_search($patternState, Config::$options["patternStates"]); + + // if this is a true pattern state update various patterns + if ($patternStateDigit !== false) { + + foreach (PatternData::$store as $patternStoreKey2 => $patternStoreData2) { + + if (($patternStoreData2["category"] == "pattern") && isset($patternStoreData2["lineagesR"])) { + + foreach ($patternStoreData2["lineagesR"] as $patternCheckInfo) { + + $lineagePatternPartial = $patternCheckInfo["lineagePattern"]; + + // if the found pattern's lineage is empty and the pattern state isn't the last (e.g. complete) add the pattern state + // otherwise, if the pattern state is less than the one being checked update the pattern + if ((PatternData::$store[$lineagePatternPartial]["state"] == "") && ($patternStateDigit != $patternStateLast)) { + + PatternData::$store[$lineagePatternPartial]["state"] = $patternState; + + } else { + + $patternStateCheck = array_search(PatternData::$store[$lineagePatternPartial]["state"], Config::$options["patternStates"]); + if ($patternStateDigit < $patternStateCheck) { + PatternData::$store[$lineagePatternPartial]["state"] = $patternState; + } + + } + + } + + } + + } + + } + + } + + } + + } + + // make sure we update the lineages with the pattern state if appropriate + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if ($patternStoreData["category"] == "pattern") { + + if (isset($patternStoreData["lineages"])) { + + foreach ($patternStoreData["lineages"] as $patternLineageKey => $patternLineageInfo) { + + $lineagePattern = $patternLineageInfo["lineagePattern"]; + $patternState = PatternData::$store[$lineagePattern]["state"]; + if (($patternState != "") && ($patternState != null)) { + PatternData::$store[$patternStoreKey]["lineages"][$patternLineageKey]["lineageState"] = $patternState; + } + + } + + } + + if (isset($patternStoreData["lineagesR"])) { + + foreach ($patternStoreData["lineagesR"] as $patternLineageKey => $patternLineageInfo) { + + $lineagePattern = $patternLineageInfo["lineagePattern"]; + $patternState = PatternData::$store[$lineagePattern]["state"]; + if (($patternState != "") && ($patternState != null)) { + PatternData::$store[$patternStoreKey]["lineages"][$patternLineageKey]["lineageState"] = $patternState; + } + + } + + } + + } + + } + + } + +} From fcfc4a94fb4df0642259e0cbc511ae2777d2f5aa Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:43:29 -0400 Subject: [PATCH 104/166] kss plugin to modify pattern data --- .../Helpers/Plugins/KSSHelperPlugin.php | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php new file mode 100644 index 000000000..cbb6d2be2 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -0,0 +1,95 @@ +patternPaths = $options["patternPaths"]; + + } + + public function run() { + + $options = array(); + $options["patternPaths"] = $this->patternPaths; + PatternEngine::setup($options); + + $kss = KSS::parse(Config::$options["sourceDir"]); + + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { + + if ($patternStoreData["category"] == "pattern") { + + if ($kssSection = $kss->getSection($patternStoreKey)) { + + PatternData::$store[$patternStoreKey]["name"] = $kssSection->getTitle(); + PatternData::$store[$patternStoreKey]["desc"] = $kssSection->getDescription(); + PatternData::$store[$patternStoreKey]["descExists"] = true; + $modifiers = $kssSection->getModifiers(); + + if (!empty($modifiers)) { + + PatternData::$store[$patternStoreKey]["modifiersExist"] = true; + $patternModifiers = array(); + + foreach ($modifiers as $modifier) { + + $name = $modifier->getName(); + $class = $modifier->getClassName(); + $desc = $modifier->getDescription(); + $code = ""; + $modifierCodeExists = false; + + if ($name[0] != ":") { + + $data = Data::getPatternSpecificData($patternStoreKey,$patternFooterData); + $data = array_merge($data,array("styleModifier" => $class)); + $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; + $code = Render::Pattern($srcPath,$patternStoreKey,$data); + + $modifierCodeExists = true; + } + + $patternModifiers[] = array("modifierName" => $name, + "modifierDesc" => $desc, + "modifierCode" => $code, + "modifierCodeExists" => $modifierCodeExists); + } + + PatternData::$store[$patternStoreKey]["modifiers"] = $patternModifiers; + + } + + } + + } + + } + + unset($patternLoader); + unset($patternLoaderInstance); + unset($kss); + + } + +} + From 4dffc4f2cf328c7488b0991dbedc320b1aaf2d26 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:43:40 -0400 Subject: [PATCH 105/166] unfinished css helper plugin --- .../Helpers/Plugins/CSSRuleSaverHelperPlugin.php | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php new file mode 100644 index 000000000..57b6cb031 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php @@ -0,0 +1,8 @@ +// set-up the mark-up for CSS Rule Saver so it can figure out which rules to save +$patternCSSExists = $this->enableCSS; +$patternCSS = ""; +if ($this->enableCSS) { + $this->cssRuleSaver->loadHTML($patternCodeRaw,false); + $patternCSS = $this->cssRuleSaver->saveRules(); + $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; +} \ No newline at end of file From a442022161dfac39ed470f3c308490d9badf73a2 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:44:34 -0400 Subject: [PATCH 106/166] exporters to handle changing pattern data for front-end view --- .../Exporters/DataLinkExporter.php | 40 +++++ .../Exporters/DataMergeExporter.php | 54 +++++++ .../Exporters/NavItemsExporter.php | 145 ++++++++++++++++++ .../Exporters/PatternPartialsExporter.php | 78 ++++++++++ .../Exporters/PatternPathDestsExporter.php | 51 ++++++ .../Exporters/PatternPathSrcExporter.php | 52 +++++++ .../Exporters/ViewAllPathsExporter.php | 66 ++++++++ 7 files changed, 486 insertions(+) create mode 100644 core/lib/PatternLab/PatternData/Exporters/DataLinkExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php create mode 100644 core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php diff --git a/core/lib/PatternLab/PatternData/Exporters/DataLinkExporter.php b/core/lib/PatternLab/PatternData/Exporters/DataLinkExporter.php new file mode 100644 index 000000000..84c2d08df --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/DataLinkExporter.php @@ -0,0 +1,40 @@ + $patternStoreData) { + + if ($patternStoreData["category"] == "pattern") { + + Data::$store["link"][$patternStoreKey] = "../../".$patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"; + + } + + } + + } + +} diff --git a/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php b/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php new file mode 100644 index 000000000..ec377a875 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php @@ -0,0 +1,54 @@ + $patternStoreData) { + + if ($patternStoreData["category"] == "pattern") { + + if (isset($patternStoreData["data"]) || isset($patternStoreData["listItems"])) { + Data::$store["patternSpecific"][$patternStoreKey] = array(); + } + + if (isset($patternStoreData["data"])) { + Data::$store["patternSpecific"][$patternStoreKey]["data"] = $patternStoreData["data"]; + } + + if (isset($patternStoreData["listItems"])) { + Data::$store["patternSpecific"][$patternStoreKey]["listItems"] = $patternStoreData["listItems"]; + } + + } + + } + + // walk across the data and change link.pattern-partial to real source + array_walk_recursive(Data::$store,'\PatternLab\Util::compareReplace'); + + } + +} diff --git a/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php b/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php new file mode 100644 index 000000000..1a8adb44c --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php @@ -0,0 +1,145 @@ + $patternStoreData) { + + if ($patternStoreData["category"] == "patternType") { + + $bi = (count($navItems["patternTypes"]) == 0) ? 0 : $bi + 1; + + // add a new patternType to the nav + $navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternStoreData["nameClean"]), + "patternTypeUC" => ucwords($patternStoreData["nameClean"]), + "patternType" => $patternStoreData["name"], + "patternTypeDash" => $patternStoreData["nameDash"], + "patternTypeItems" => array(), + "patternItems" => array()); + + // starting a new set of pattern types. it might not have any pattern subtypes + $patternSubtypeSet = false; + $patternType = $patternStoreData["name"]; + $patternTypeDash = $patternStoreData["nameDash"]; + + } else if ($patternStoreData["category"] == "patternSubtype") { + + $ni = (!$patternSubtypeSet) ? 0 : $ni + 1; + + // add a new patternSubtype to the nav + $navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternStoreData["nameClean"]), + "patternSubtypeUC" => ucwords($patternStoreData["nameClean"]), + "patternSubtype" => $patternStoreData["name"], + "patternSubtypeDash" => $patternStoreData["nameDash"], + "patternSubtypeItems" => array()); + + // starting a new set of pattern types. it might not have any pattern subtypes + $patternSubtype = $patternStoreData["name"]; + $patternSubtypeDash = $patternStoreData["nameDash"]; + $patternSubtypeSet = true; + + } else if ($patternStoreData["category"] == "pattern") { + + if (!$patternStoreData["hidden"]) { + + // set-up the info for the nav + $patternInfo = array("patternPath" => $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html", + "patternSrcPath" => $patternStoreData["pathName"], + "patternName" => ucwords($patternStoreData["nameClean"]), + "patternState" => $patternStoreData["state"], + "patternPartial" => $patternStoreData["partial"]); + + // add to the nav + if ($patternStoreData["depth"] == 1) { + $navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; + } else { + $navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; + } + + } + + } + + } + + // review each subtype. add a view all link or remove the subtype as necessary + foreach ($navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { + + $reset = false; + $patternType = $patternTypeValues["patternType"]; + $patternTypeDash = $patternTypeValues["patternTypeDash"]; + + if (!in_array($patternType,Config::$options["styleGuideExcludes"])) { + + foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { + + // if there are no sub-items in a section remove it + if (empty($patternSubtypeValues["patternSubtypeItems"])) { + + unset($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]); + $reset = true; + + } else { + + $patternSubtype = $patternSubtypeValues["patternSubtype"]; + $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; + $subItemsCount = count($patternSubtypeValues["patternSubtypeItems"]); + + // add a view all link + $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$subItemsCount] = array( + "patternPath" => $patternType."-".$patternSubtype."/index.html", + "patternName" => "View All", + "patternType" => $patternType, + "patternSubtype" => $patternSubtype, + "patternPartial" => "viewall-".$patternTypeDash."-".$patternSubtypeDash); + + } + + } + + } + + if ($reset) { + $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"] = array_values($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"]); + $reset = false; + } + + } + + return $navItems; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php new file mode 100644 index 000000000..144ff3b98 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php @@ -0,0 +1,78 @@ + $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && (!$patternStoreData["hidden"]) && ($patternStoreData["depth"] == 2) && (!in_array($patternStoreData["type"],Config::$options["styleGuideExcludes"]))) { + + if ((empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["subtype"] == $subtype))) { + + $patternPartialData = array(); + $patternPartialData["patternName"] = ucwords($patternStoreData["nameClean"]); + $patternPartialData["patternLink"] = $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"; + $patternPartialData["patternPartial"] = $patternStoreData["partial"]; + + $patternPartialData["patternLineageExists"] = isset($patternStoreData["lineages"]); + $patternPartialData["patternLineages"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); + $patternPartialData["patternLineageRExists"] = isset($patternStoreData["lineagesR"]); + $patternPartialData["patternLineagesR"] = isset($patternStoreData["lineagesR"]) ? $patternStoreData["lineagesR"] : array(); + $patternPartialData["patternLineageEExists"] = (isset($patternStoreData["lineages"]) || isset($patternStoreData["lineagesR"])); + + $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]); + $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; + + $patternPartialData["patternModifiersExists"] = isset($patternStoreData["modifiers"]); + $patternPartialData["patternModifiersExists"] = isset($patternStoreData["modifiers"]) ? $patternStoreData["modifiers"] : array(); + + $patternPartialData["patternCSSExists"] = Config::$options["enableCSS"]; + + $patternPartials[] = $patternPartialData; + + } + + } + + } + + return array("partials" => $patternPartials); + + } + +} + + diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php new file mode 100644 index 000000000..29a50d40b --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php @@ -0,0 +1,51 @@ + $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { + + $nameDash = $patternStoreData["nameDash"]; + $typeDash = $patternStoreData["typeDash"]; + + if (!isset($patternPathDests[$typeDash])) { + $patternPathDests[$typeDash] = array(); + } + + $patternPathDests[$typeDash][$nameDash] = $patternStoreData["pathDash"]; + + } + + } + + return $patternPathDests; + + } + +} diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php new file mode 100644 index 000000000..d8050a613 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php @@ -0,0 +1,52 @@ + $patternStoreData) { + + if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { + + $nameDash = $patternStoreData["nameDash"]; + $typeDash = $patternStoreData["typeDash"]; + + if (!isset($patternPathDests[$typeDash])) { + $patternPathDests[$typeDash] = array(); + } + + $patternPathDests[$typeDash][$nameDash] = $patternStoreData["pathName"]; + + } + + } + + return $patternPathDests; + + } + +} diff --git a/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php b/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php new file mode 100644 index 000000000..1e7856344 --- /dev/null +++ b/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php @@ -0,0 +1,66 @@ + $patternTypeValues) { + + $patternType = $patternTypeValues["patternType"]; + $patternTypeDash = $patternTypeValues["patternTypeDash"]; + + if (!in_array($patternType,Config::$options["styleGuideExcludes"])) { + + foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { + + $patternSubtype = $patternSubtypeValues["patternSubtype"]; + $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; + + if (isset($patternSubtypeValues["patternSubtypeItems"])) { + + foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItemValues) { + + if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { + + $viewAllPaths[$patternTypeDash][$patternSubtypeDash] = $patternType."-".$patternSubtype; + + } + + } + + } + + } + + } + + } + + return $viewAllPaths; + + } + +} \ No newline at end of file From 4d6409e610951c8bd976ddf5be6b6cc06adcfcbc Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:45:09 -0400 Subject: [PATCH 107/166] pattern engine info to draw patterns --- core/lib/PatternLab/PatternEngine.php | 55 +++++++++++++++++++ core/lib/PatternLab/PatternEngine/Rule.php | 33 +++++++++++ .../PatternEngine/Rules/MustacheRule.php | 38 +++++++++++++ .../PatternEngine/Rules/TwigRule.php | 37 +++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 core/lib/PatternLab/PatternEngine.php create mode 100644 core/lib/PatternLab/PatternEngine/Rule.php create mode 100644 core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php create mode 100644 core/lib/PatternLab/PatternEngine/Rules/TwigRule.php diff --git a/core/lib/PatternLab/PatternEngine.php b/core/lib/PatternLab/PatternEngine.php new file mode 100644 index 000000000..815f9f176 --- /dev/null +++ b/core/lib/PatternLab/PatternEngine.php @@ -0,0 +1,55 @@ +test()) { + $found = true; + self::$patternLoader = $rule->getInstance($options); + } + } + + if (!$found) { + print "the supplied pattern extension didn't match a pattern loader rule. please check.\n"; + exit; + } + + } + + /** + * Load all of the rules related to Pattern Engine + */ + public static function loadRules($options) { + + foreach (glob(__DIR__."/PatternEngine/Rules/*.php") as $filename) { + $rule = str_replace(".php","",str_replace(__DIR__."/PatternEngine/Rules/","",$filename)); + $ruleClass = "\PatternLab\PatternEngine\Rules\\".$rule; + self::$rules[] = new $ruleClass($options); + } + + } + +} diff --git a/core/lib/PatternLab/PatternEngine/Rule.php b/core/lib/PatternLab/PatternEngine/Rule.php new file mode 100644 index 000000000..c063a279e --- /dev/null +++ b/core/lib/PatternLab/PatternEngine/Rule.php @@ -0,0 +1,33 @@ +engineProp == Config::$options["patternExtension"]); + + } + +} diff --git a/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php b/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php new file mode 100644 index 000000000..3cd18f33a --- /dev/null +++ b/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php @@ -0,0 +1,38 @@ +engineProp = "mustache"; + + } + + public function getInstance($options) { + + $options["loader"] = new MustacheLoader(__DIR__."/../../".Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); + $options["partial_loader"] = new MustacheLoader(__DIR__."/../../".Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); + + return new \Mustache_Engine($options); + + } + +} diff --git a/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php b/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php new file mode 100644 index 000000000..2e3f2c258 --- /dev/null +++ b/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php @@ -0,0 +1,37 @@ +engineProp = "twig"; + + } + + public function getInstance() { + + $options = new TwigLoader(Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); + + return new \Twig_Environment($options); + + } + +} From ecb6044801c7d14ba4d7a1a236feb74a7785559c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:45:22 -0400 Subject: [PATCH 108/166] KSS parser --- core/lib/PatternLab/Parsers/Plugins/KSS.php | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 core/lib/PatternLab/Parsers/Plugins/KSS.php diff --git a/core/lib/PatternLab/Parsers/Plugins/KSS.php b/core/lib/PatternLab/Parsers/Plugins/KSS.php new file mode 100644 index 000000000..95f5cc4c0 --- /dev/null +++ b/core/lib/PatternLab/Parsers/Plugins/KSS.php @@ -0,0 +1,30 @@ + Date: Fri, 30 May 2014 22:45:38 -0400 Subject: [PATCH 109/166] generic exporter class --- core/lib/PatternLab/KSSParser.php | 24 -------------------- core/lib/PatternLab/PatternData/Exporter.php | 21 +++++++++++++++++ 2 files changed, 21 insertions(+), 24 deletions(-) delete mode 100644 core/lib/PatternLab/KSSParser.php create mode 100644 core/lib/PatternLab/PatternData/Exporter.php diff --git a/core/lib/PatternLab/KSSParser.php b/core/lib/PatternLab/KSSParser.php deleted file mode 100644 index 3f4084ddc..000000000 --- a/core/lib/PatternLab/KSSParser.php +++ /dev/null @@ -1,24 +0,0 @@ - Date: Fri, 30 May 2014 22:46:01 -0400 Subject: [PATCH 110/166] generic pattern type rule --- .../PatternData/Rules/PatternTypeRule.php | 61 +++++++++++++++++ .../PatternInfoRules/PatternTypeRule.php | 66 ------------------- 2 files changed, 61 insertions(+), 66 deletions(-) create mode 100644 core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php delete mode 100644 core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php diff --git a/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php b/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php new file mode 100644 index 000000000..dfe27b5ad --- /dev/null +++ b/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php @@ -0,0 +1,61 @@ +depthProp = 0; + $this->extProp = ""; + $this->isDirProp = true; + $this->isFileProp = false; + $this->searchProp = ""; + $this->ignoreProp = ""; + + } + + public function run($depth, $ext, $path, $pathName, $name) { + + // set-up the names + $patternType = $name; // 00-atoms + $patternTypeDash = $this->getPatternName($name,false); // atoms + $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) + + // create a key for the data store + $patternStoreKey = $patternTypeDash."-pltype"; + + // add a new patternType to the nav + PatternData::$store[$patternStoreKey] = array("category" => "patternType", + "name" => $patternType, + "nameDash" => $patternTypeDash, + "nameClean" => $patternTypeClean, + "depth" => $depth, + "ext" => $ext, + "path" => $path, + "pathName" => $pathName, + "isDir" => $this->isDirProp , + "isFile" => $this->isFileProp); + + // starting a new set of pattern types. it might not have any pattern subtypes + PatternData::$patternType = $patternType; + PatternData::$patternTypeClean = $patternTypeClean; + PatternData::$patternTypeDash = $patternTypeDash; + + } + +} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php b/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php deleted file mode 100644 index b12e7e073..000000000 --- a/core/lib/PatternLab/PatternInfoRules/PatternTypeRule.php +++ /dev/null @@ -1,66 +0,0 @@ -depthProp = 0; - $this->extProp = ""; - $this->isDirProp = true; - $this->isFileProp = false; - $this->searchProp = ""; - - } - - public function runRule($depth, $ext, $path, $pathName, $name) { - - PatternInfo::$bi = (count(PatternInfo::$navItems["patternTypes"]) == 0) ? 0 : PatternInfo::$bi + 1; - $bi = PatternInfo::$bi; - - // set-up the names - $patternType = $name; // 00-atoms - $patternTypeDash = $this->getPatternName($name,false); // atoms - $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) - - // add to pattern types & pattern paths - $patternTypes[] = $patternType; - $patternPaths[$patternTypeDash] = array(); - - // add a new patternType to the nav - PatternInfo::$navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternTypeClean), - "patternTypeUC" => ucwords($patternTypeClean), - "patternType" => $patternType, - "patternTypeDash" => $patternTypeDash, - "patternTypeItems" => array(), - "patternItems" => array()); - - // starting a new set of pattern types. it might not have any pattern subtypes - PatternInfo::$patternSubtypeSet = false; - PatternInfo::$patternType = $patternType; - PatternInfo::$patternTypeDash = $patternTypeDash; - - } - -} \ No newline at end of file From 30c3f69139dc337e9c45533342632b7d0dfed38f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:46:53 -0400 Subject: [PATCH 111/166] updating config options now that it's a static class --- core/builder.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/core/builder.php b/core/builder.php index 9568e6062..26e4204dc 100644 --- a/core/builder.php +++ b/core/builder.php @@ -77,8 +77,7 @@ // run commands // load Pattern Lab's config, if first time set-up move files appropriately too - $configurer = new PatternLab\Configurer; - $config = $configurer->getConfig(); + PatternLab\Config::loadOptions(); // set-up required vars $enableCSS = $console->findCommandOption("c|enablecss"); @@ -89,7 +88,7 @@ if (($command == "g") || ($command == "b")) { // load the generator - $g = new PatternLab\Generator($config); + $g = new PatternLab\Generator(); $g->generate($enableCSS,$moveStatic,$noCacheBuster); $g->printSaying(); @@ -99,31 +98,31 @@ $enableCSS = false; // load the generator - $g = new PatternLab\Generator($config); + $g = new PatternLab\Generator(); $g->generate($enableCSS,$moveStatic,$noCacheBuster); // load the watcher - $w = new PatternLab\Watcher($config); + $w = new PatternLab\Watcher(); $w->watch($autoReload,$moveStatic,$noCacheBuster); } else if ($command == "s") { // run the snapshot command $snapshotDir = $console->findCommandOptionValue("d|dir"); - $s = new PatternLab\Snapshot($config); + $s = new PatternLab\Snapshot(); $s->takeSnapshot($snapshotDir); } else if ($command == "f") { // run the snapshot command $starterKit = $console->findCommandValue("f|fetch"); - $sk = new PatternLab\StarterKit($config); + $sk = new PatternLab\StarterKit(); $sk->fetch($starterKit); } else if ($command == "v") { // write out the version number - print "You're running v".$config["v"]." of the PHP version of Pattern Lab.\n"; + print "You're running v".PatternLab\Config::$options["v"]." of the PHP version of Pattern Lab.\n"; exit; } From f59ac46ac6e71afa89338ef8a66d13e99e074afb Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:47:14 -0400 Subject: [PATCH 112/166] updating intro copy --- core/lib/PatternLab/Builder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index efa309624..0da7a0ee6 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -1,12 +1,12 @@ Date: Fri, 30 May 2014 22:47:25 -0400 Subject: [PATCH 113/166] including the various classes now used in builder --- core/lib/PatternLab/Builder.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 0da7a0ee6..7a2a9fd65 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -12,6 +12,15 @@ namespace PatternLab; +use \PatternLab\Config; +use \PatternLab\Data; +use \PatternLab\PatternData\Exporters\NavItemsExporter; +use \PatternLab\PatternData\Exporters\PatternPartialsExporter; +use \PatternLab\PatternData\Exporters\PatternPathDestsExporter; +use \PatternLab\PatternData\Exporters\ViewAllPathsExporter; +use \PatternLab\Render; +use \PatternLab\Template\Helper; + class Builder { /** From 631fba71784446c48d00d7d4bf6588a7180b42d7 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:47:40 -0400 Subject: [PATCH 114/166] slimming down the builder --- core/lib/PatternLab/Builder.php | 1137 +++---------------------------- 1 file changed, 106 insertions(+), 1031 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 7a2a9fd65..6c0ec60aa 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -24,465 +24,14 @@ class Builder { /** - * When initializing the Builder class or the sub-classes make sure the base properties are configured - * Also, create the config if it doesn't already exist + * When initializing the Builder class make sure the template helper is set-up */ - public function __construct($config = array()) { + public function __construct() { - // making sure the config isn't empty - if (empty($config)) { - print "A set of configuration options is required to use Pattern Lab.\n"; - exit; - } - - // set-up the source & public dirs - $this->sp = "/../../../".$config["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; - $this->pp = "/../../../".$config["publicDir"]."/patterns".DIRECTORY_SEPARATOR; - $this->sd = __DIR__."/../../../".$config["sourceDir"]; - $this->pd = __DIR__."/../../../".$config["publicDir"]; - - // populate some standard variables out of the config - foreach ($config as $key => $value) { - - // if the variables are array-like make sure the properties are validated/trimmed/lowercased before saving - $arrayKeys = array("ie","id","patternStates","styleGuideExcludes"); - if (in_array($key,$arrayKeys)) { - $values = explode(",",$value); - array_walk($values,'PatternLab\Builder::trim'); - $this->$key = $values; - } else if ($key == "ishControlsHide") { - $this->$key = new \stdClass(); - if ($value != "") { - $values = explode(",",$value); - foreach($values as $value2) { - $value2 = trim($value2); - $this->$key->$value2 = true; - } - } - if ($this->pageFollowNav == "false") { - $value = "tools-follow"; - $this->$key->$value = true; - } - if ($this->autoReloadNav == "false") { - $value = "tools-reload"; - $this->$key->$value = true; - } - $toolssnapshot = "tools-snapshot"; // i was an idgit and used dashes - if (!isset($this->$key->$toolssnapshot)) { - if (!is_dir($this->pd."/snapshots")) { - $this->$key->$toolssnapshot = true; - } - } - } else { - $this->$key = $value; - } - - } - - // provide the default for enable CSS. performance hog so it should be run infrequently - $this->enableCSS = false; - $this->patternCSS = array(); - - // find the pattern extension - $this->patternExtension = $this->patternEngine; - - } - - /** - * Renders a given pattern file using Mustache and incorporating the provided data - * @param {String} the filename of the file to be rendered - * @param {String} the pattern partial - * - * @return {String} the mark-up as rendered by Mustache - */ - protected function renderPattern($f,$p,$k = array()) { - - // if there is pattern-specific data make sure to override the default in $this->d - $d = $this->d; - - if (isset($d["patternSpecific"]) && array_key_exists($p,$d["patternSpecific"])) { - - if (!empty($d["patternSpecific"][$p]["data"])) { - $d = array_replace_recursive($d, $d["patternSpecific"][$p]["data"]); - } - - if (!empty($d["patternSpecific"][$p]["listItems"])) { - - $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); - - $k = 0; - $c = count($d["patternSpecific"][$p]["listItems"]); - - while ($k < $c) { - $section = $numbers[$k]; - $d["listItems"][$section] = array_replace_recursive( $d["listItems"][$section], $d["patternSpecific"][$p]["listItems"][$section]); - $k++; - } - - } - - } - - if (count($k) > 0) { - $d = array_merge($d, $k); - } - - $pattern = $this->pl->render($f,$d); - $escaped = htmlentities($pattern); - - if ($this->addPatternHF) { - $patternHead = $this->mv->render($this->patternHead,$d); - $patternFoot = $this->mv->render($this->patternFoot,$d); - $pattern = $patternHead.$pattern.$patternFoot; - } - - return array($pattern,$escaped); - - } - - /** - * Generates the index page and style guide - */ - protected function generateMainPages() { - - // make sure $this->mfs & $this->mv are refreshed - $templateLoader = new TemplateLoader(); - $this->mfs = $templateLoader->fileSystem(); - $this->mv = $templateLoader->vanilla(); - - // get the source pattern paths - $patternPathDests = array(); - foreach($this->patternPaths as $patternType => $patterns) { - $patternPathDests[$patternType] = array(); - foreach ($patterns as $pattern => $patternInfo) { - if ($patternInfo["render"]) { - $patternPathDests[$patternType][$pattern] = $patternInfo["patternDestPath"]; - } - } - } - - // render out the main pages and move them to public - $this->navItems['autoreloadnav'] = $this->autoReloadNav; - $this->navItems['autoreloadport'] = $this->autoReloadPort; - $this->navItems['pagefollownav'] = $this->pageFollowNav; - $this->navItems['pagefollowport'] = $this->pageFollowPort; - $this->navItems['patternpaths'] = json_encode($patternPathDests); - $this->navItems['viewallpaths'] = json_encode($this->viewAllPaths); - $this->navItems['mqs'] = $this->gatherMQs(); - $this->navItems['qrcodegeneratoron'] = $this->qrCodeGeneratorOn; - $this->navItems['ipaddress'] = getHostByName(getHostName()); - $this->navItems['xiphostname'] = $this->xipHostname; - $this->navItems['ishminimum'] = $this->ishMinimum; - $this->navItems['ishmaximum'] = $this->ishMaximum; - $this->navItems['ishControlsHide'] = $this->ishControlsHide; - - // grab the partials into a data object for the style guide - $sd = array("partials" => array()); - foreach ($this->patternPartials as $patternSubtypes) { - foreach ($patternSubtypes as $patterns) { - $sd["partials"][] = $patterns; - } - } - - // render the "view all" pages - $this->generateViewAllPages(); - - // add cacheBuster info - $this->navItems['cacheBuster'] = $this->cacheBuster; - $sd['cacheBuster'] = $this->cacheBuster; - - // render the index page - $r = $this->mfs->render('index',$this->navItems); - file_put_contents($this->pd."/index.html",$r); - - // render the style guide - $sd = array_replace_recursive($this->d,$sd); - $styleGuideHead = $this->mv->render($this->mainPageHead,$sd); - $styleGuideFoot = $this->mv->render($this->mainPageFoot,$sd); - $styleGuidePage = $styleGuideHead.$this->mfs->render('viewall',$sd).$styleGuideFoot; + //$this->patternCSS = array(); - if (!is_dir($this->pd."/styleguide/html/")) { - print "ERROR: the main style guide wasn't written out. make sure public/styleguide exists. can copy core/styleguide\n"; - } else { - file_put_contents($this->pd."/styleguide/html/styleguide.html",$styleGuidePage); - } - - } - - /** - * Generates all of the patterns and puts them in the public directory - */ - protected function generatePatterns() { - - // make sure patterns exists - if (!is_dir($this->pd."/patterns")) { - mkdir($this->pd."/patterns"); - } - - // make sure the pattern header & footer are added - $this->addPatternHF = true; - - // make sure $this->pl & $this->mv are refreshed - $patternLoader = new PatternLoader($this->patternPaths); - $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); - - $templateLoader = new TemplateLoader(); - $this->mv = $templateLoader->vanilla(); - - // loop over the pattern paths to generate patterns for each - foreach($this->patternPaths as $patternType) { - - foreach($patternType as $pattern => $pathInfo) { - - // make sure this pattern should be rendered - if ($pathInfo["render"]) { - - // get the rendered, escaped, and mustache pattern - $this->generatePatternFile($pathInfo["patternSrcPath"].".".$this->patternExtension,$pathInfo["patternPartial"],$pathInfo["patternDestPath"],$pathInfo["patternState"]); - - } - - } - - } - - } - - /** - * Generates a pattern with a header & footer, the escaped version of a pattern, the msutache template, and the css if appropriate - * @param {String} the filename of the file to be rendered - * @param {String} the pattern partial - * @param {String} path where the files need to be written too - * @param {String} pattern state - */ - private function generatePatternFile($f,$p,$path,$options) { - - // the core footer isn't rendered as mustache but we have some variables there any way. find & replace. - $patternFooterData = array("patternFooterData" => array()); - $patternFooterData["patternFooterData"]["cssEnabled"] = ($this->enableCSS && isset($this->patternCSS[$p])) ? "true" : "false"; - $patternFooterData["patternFooterData"]["lineage"] = json_encode($this->patternLineages[$p]); - $patternFooterData["patternFooterData"]["lineageR"] = json_encode($this->patternLineagesR[$p]); - $patternFooterData["patternFooterData"]["patternBreadcrumb"] = $options["patternBreadcrumb"]; - $patternFooterData["patternFooterData"]["patternDesc"] = $options["patternDesc"]; - $patternFooterData["patternFooterData"]["patternEngine"] = $options["patternEngine"]; - $patternFooterData["patternFooterData"]["patternName"] = $options["patternName"]; - $patternFooterData["patternFooterData"]["patternPartial"] = $p; - $patternFooterData["patternFooterData"]["patternState"] = $options["state"]; - - // render the pattern and return it as well as the encoded version - list($rf,$e) = $this->renderPattern($f,$p,$patternFooterData); - - // get the original mustache template - $m = htmlentities(file_get_contents(__DIR__.$this->sp.$f)); - - // if the pattern directory doesn't exist create it - if (!is_dir(__DIR__.$this->pp.$path)) { - mkdir(__DIR__.$this->pp.$path); - } - - // write out the various pattern files - file_put_contents(__DIR__.$this->pp.$path."/".$path.".html",$rf); - file_put_contents(__DIR__.$this->pp.$path."/".$path.".escaped.html",$e); - file_put_contents(__DIR__.$this->pp.$path."/".$path.".".$this->patternExtension,$m); - if ($this->enableCSS && isset($this->patternCSS[$p])) { - file_put_contents(__DIR__.$this->pp.$path."/".$path.".css",htmlentities($this->patternCSS[$p])); - } - - } - - /** - * Generates the view all pages - */ - protected function generateViewAllPages() { - - // make sure $this->mfs & $this->mv are refreshed on each generation of view all - $templateLoader = new TemplateLoader(); - $this->mfs = $templateLoader->fileSystem(); - $this->mv = $templateLoader->vanilla(); - - // add view all to each list - $i = 0; $k = 0; - foreach ($this->navItems['patternTypes'] as $bucket) { - - // make sure that the navItems index exists. catches issues with pages & templates - if (isset($bucket["patternTypeItems"])) { - - foreach ($bucket["patternTypeItems"] as $navItem) { - - // make sure the navSubItems index exists. catches issues with empty folders - if (isset($navItem["patternSubtypeItems"])) { - - foreach ($navItem["patternSubtypeItems"] as $subItem) { - - if ($subItem["patternName"] == "View All") { - - // get the pattern parts - $patternType = $subItem["patternType"]; - $patternSubType = $subItem["patternSubtype"]; - - // get all the rendered partials that match - $sid = array("partials" => $this->patternPartials[$this->getPatternName($patternType,false)."-".$this->getPatternName($patternSubType,false)]); - $sid["patternPartial"] = $subItem["patternPartial"]; - $sid["cacheBuster"] = $this->cacheBuster; - $sid = array_replace_recursive($this->d,$sid); - - // render the viewall template - $viewAllHead = $this->mv->render($this->mainPageHead,$sid); - $viewAllFoot = $this->mv->render($this->mainPageFoot,$sid); - $viewAllPage = $viewAllHead.$this->mfs->render('viewall',$sid).$viewAllFoot; - - // if the pattern directory doesn't exist create it - $patternPath = $patternType."-".$patternSubType; - if (!is_dir(__DIR__.$this->pp.$patternPath)) { - mkdir(__DIR__.$this->pp.$patternPath); - file_put_contents(__DIR__.$this->pp.$patternPath."/index.html",$viewAllPage); - } else { - file_put_contents(__DIR__.$this->pp.$patternPath."/index.html",$viewAllPage); - } - - } - - } - - } - - } - - } - - $i++; - $k = 0; - - } - - } - - /** - * Gather data from source/_data/_data.json, source/_data/_listitems.json, and pattern-specific json files - * - * Reserved attributes: - * - $this->d["listItems"] : listItems from listitems.json, duplicated into separate arrays for $this->d->listItems->one, $this->d->listItems->two, $this->d->listItems->three... etc. - * - $this->d["link"] : the links to each pattern - * - $this->d["cacheBuster"] : the cache buster value to be appended to URLs - * - $this->d["patternSpecific"] : holds attributes from the pattern-specific data files - * - * @return {Array} populates $this->d - */ - protected function gatherData() { - - // set the cacheBuster - $this->cacheBuster = ($this->noCacheBuster || ($this->cacheBusterOn == "false")) ? 0 : time(); - - // gather the data from the main source data.json - if (file_exists($this->sd."/_data/_data.json")) { - $data = file_get_contents($this->sd."/_data/_data.json"); - $this->d = json_decode($data,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg("_data/_data.json",$jsonErrorMessage,$data); - } - } else { - print "Missing a required file, source/_data/_data.json. Aborting.\n"; - exit; - } - - if (is_array($this->d)) { - $reservedKeys = array("listItems","cacheBuster","link","patternSpecific","patternFooterData"); - foreach ($reservedKeys as $reservedKey) { - if (array_key_exists($reservedKey,$this->d)) { - print "\"".$reservedKey."\" is a reserved key in Pattern Lab. The data using that key in _data.json will be overwritten. Please choose a new key.\n"; - } - } - } - - $this->d["listItems"] = $this->getListItems($this->sd."/_data/_listitems.json"); - $this->d["cacheBuster"] = $this->cacheBuster; - $this->d["link"] = array(); - $this->d["patternSpecific"] = array(); - - } - - /** - * Finds the regular and reverse lineages for the patterns - * - * @return {Array} an array of patterns with their lineages - */ - protected function gatherLineages() { - - $this->patternLineages = array(); - $this->patternLineagesR = array(); - $foundLineages = array(); - - // check for the regular lineages - foreach($this->patternPaths as $patternType => $patterns) { - - foreach ($patterns as $pattern => $patternInfo) { - - $patternLineage = array(); - $filename = $patternInfo["patternSrcPath"]; - - // if a file doesn't exist it assumes it's a pseudo-pattern and will use the last lineage found - if (file_exists(__DIR__.$this->sp.$filename.".".$this->patternExtension)) { - $foundLineages = array_unique($this->getLineage($filename)); - } - - if (count($foundLineages) > 0) { - foreach ($foundLineages as $lineage) { - $patternBits = $this->getPatternInfo($lineage); - if ((count($patternBits) == 2) && isset($this->patternPaths[$patternBits[0]][$patternBits[1]])) { - $path = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternDestPath"]; - $patternLineage[] = array("lineagePattern" => $lineage, - "lineagePath" => "../../patterns/".$path."/".$path.".html"); - } else { - if (strpos($lineage, '/') === false) { - print "You may have a typo in ".$patternInfo["patternSrcPath"].". {{> ".$lineage." }} is not a valid pattern.\n"; - } - } - } - } - - $this->patternLineages[$patternType."-".$pattern] = $patternLineage; - - } - - } - - // check for the reverse lineages - foreach ($this->patternLineages as $needlePartial => $needleLineages) { - - $patternLineageR = array(); - - foreach ($this->patternLineages as $haystackPartial => $haystackLineages) { - - foreach ($haystackLineages as $haystackLineage) { - - if ($haystackLineage["lineagePattern"] == $needlePartial) { - - $foundAlready = false; - foreach ($patternLineageR as $patternCheck) { - if ($patternCheck["lineagePattern"] == $haystackPartial) { - $foundAlready = true; - break; - } - } - - if (!$foundAlready) { - $patternBits = $this->getPatternInfo($haystackPartial); - if (isset($this->patternPaths[$patternBits[0]][$patternBits[1]])) { - $path = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternDestPath"]; - $patternLineageR[] = array("lineagePattern" => $haystackPartial, - "lineagePath" => "../../patterns/".$path."/".$path.".html"); - } - } - - } - - } - - } - - $this->patternLineagesR[$needlePartial] = $patternLineageR; - - } + // set-up the various attributes for rendering templates + Helper::setup(); } @@ -495,8 +44,8 @@ protected function gatherMQs() { $mqs = array(); - foreach(glob($this->sd."/css/*.css") as $filename) { - $data = file_get_contents($filename); + foreach(glob(Config::$options["sourceDir"]."/css/*.css") as $filename) { + $data = file_get_contents($filename); preg_match_all("/(min|max)-width:([ ]+)?(([0-9]{1,5})(\.[0-9]{1,20}|)(px|em))/",$data,$matches); foreach ($matches[3] as $match) { if (!in_array($match,$mqs)) { @@ -512,437 +61,142 @@ protected function gatherMQs() { } /** - * Refactoring the pattern path stuff + * Generates the index page and style guide */ - protected function gatherPatternInfo() { - - // make sure the pattern header & footer aren't added - $this->addPatternHF = false; + protected function generateIndex() { + + // grab the items for the nav + $niExporter = new NavItemsExporter(); + $navItems = $niExporter->run(); + + // grab the pattern paths that will be used on the front-end + $ppdExporter = new PatternPathDestsExporter(); + $patternPathDests = $ppdExporter->run(); + + // grab the view all paths that will be used on the front-end + $vapExporter = new ViewAllPathsExporter(); + $viewAllPaths = $vapExporter->run($navItems); + + // add the various configuration options that need to be drawn on the front-end + $navItems["autoreloadnav"] = Config::$options["autoReloadNav"]; + $navItems["autoreloadport"] = Config::$options["autoReloadPort"]; + $navItems["cacheBuster"] = Config::$options["cacheBuster"]; + $navItems["ipaddress"] = getHostByName(getHostName()); + $navItems["ishminimum"] = Config::$options["ishMinimum"]; + $navItems["ishmaximum"] = Config::$options["ishMaximum"]; + $navItems["ishControlsHide"] = Config::$options["ishControlsHide"]; + $navItems["mqs"] = $this->gatherMQs(); + $navItems["pagefollownav"] = Config::$options["pageFollowNav"]; + $navItems["pagefollowport"] = Config::$options["pageFollowPort"]; + $navItems["patternpaths"] = json_encode($patternPathDests); + $navItems["qrcodegeneratoron"] = Config::$options["qrCodeGeneratorOn"]; + $navItems["viewallpaths"] = json_encode($viewAllPaths); + $navItems["xiphostname"] = Config::$options["xipHostname"]; - // gather pattern info based on the supported rules - $options = array("patternSourceDir" => __DIR__.$this->sp, "patternExtension" => $this->patternExtension); - PatternInfo::loadRules($options); - PatternInfo::gather($options); - - $kss = KSSParser::parse($this->sd); - - // initialize various arrays - $this->navItems = PatternInfo::$navItems; - $this->patternPaths = PatternInfo::$patternPaths; - $this->patternTypes = PatternInfo::$patternTypes; - $this->patternLineages = array(); - $this->patternPartials = array(); - $this->viewAllPaths = array(); - - // get all of the lineages - $this->gatherLineages(); - - // check on the states of the patterns - $patternStateLast = count($this->patternStates) - 1; - foreach($this->patternPaths as $patternType => $patterns) { - - foreach ($patterns as $pattern => $patternInfo) { - - $patternState = $this->patternPaths[$patternType][$pattern]["patternState"]; - - // make sure the pattern has a given state - if ($patternState != "") { - - $patternStateDigit = array_search($patternState, $this->patternStates); - if ($patternStateDigit !== false) { - // iterate over each of the reverse lineages for a given pattern to update their state - foreach ($this->patternLineagesR[$patternType."-".$pattern] as $patternCheckInfo) { - $patternBits = $this->getPatternInfo($patternCheckInfo["lineagePattern"]); - if (($this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"] == "") && ($patternStateDigit != $patternStateLast)) { - $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"] = $patternState; - } else { - $patternStateCheck = array_search($this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"], $this->patternStates); - if ($patternStateDigit < $patternStateCheck) { - $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"] = $patternState; - } - } - } - - } - - } - - } - - } + // render the index page + $index = Helper::$filesystemLoader->render('index',$navItems); + file_put_contents(Config::$options["publicDir"]."/index.html",$index); - // make sure we update the lineages with the pattern state if appropriate - foreach($this->patternLineages as $pattern => $patternLineages) { - foreach($patternLineages as $key => $patternLineageInfo) { - $patternBits = $this->getPatternInfo($patternLineageInfo["lineagePattern"]); - $patternState = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"]; - if (($patternState != "") && ($patternState != null)) { - $this->patternLineages[$pattern][$key]["lineageState"] = $patternState; - } - } - } + } + + /** + * Generates all of the patterns and puts them in the public directory + */ + protected function generatePatterns() { - foreach($this->patternLineagesR as $pattern => $patternLineages) { - foreach($patternLineages as $key => $patternLineageInfo) { - $patternBits = $this->getPatternInfo($patternLineageInfo["lineagePattern"]); - $patternState = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"]; - if (($patternState != "") && ($patternState != null)) { - $this->patternLineagesR[$pattern][$key]["lineageState"] = $patternState; - } - } + // make sure patterns exists + if (!is_dir(Config::$options["publicDir"]."/patterns")) { + mkdir(Config::$options["publicDir"]."/patterns"); } - // walk across the data and update links - array_walk_recursive($this->d,'PatternLab\Builder::compareReplace'); - - // make sure $this->mpl is refreshed - $patternLoader = new PatternLoader($this->patternPaths); - $this->pl = $patternLoader->loadPatternLoaderInstance($this->patternEngine,__DIR__.$this->sp); - - // run through the nav items and generate pattern partials and the view all pages - foreach ($this->navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { - - $patternType = $patternTypeValues["patternType"]; - $patternTypeDash = $patternTypeValues["patternTypeDash"]; + // loop over the pattern data store to render the individual patterns + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - // if this has a second level of patterns check them out (means we don't process pages & templates) - if ((count($patternTypeValues["patternTypeItems"]) != 0) && (!in_array($patternType,$this->styleGuideExcludes))) { + if (($patternStoreData["category"] == "pattern") && (!$patternStoreData["hidden"])) { - $arrayReset = false; + $path = $patternStoreData["pathDash"]; + $pathName = (isset($patternStoreData["pseudo"])) ? $patternStoreData["pathOrig"] : $patternStoreData["pathName"]; - foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { - - // if there are no sub-items in a section remove it, else do a bunch of other stuff - if (count($patternSubtypeValues["patternSubtypeItems"]) == 0) { - - unset($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]); - $arrayReset = true; - - } else { - - $patternSubtype = $patternSubtypeValues["patternSubtype"]; - $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; - $subItemsCount = count($patternSubtypeValues["patternSubtypeItems"]); - - // add a view all link - $this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$subItemsCount] = array( - "patternPath" => $patternType."-".$patternSubtype."/index.html", - "patternName" => "View All", - "patternType" => $patternType, - "patternSubtype" => $patternSubtype, - "patternPartial" => "viewall-".$patternTypeDash."-".$patternSubtypeDash); - - // add to the view all paths - $this->viewAllPaths[$patternTypeDash][$patternSubtypeDash] = $patternType."-".$patternSubtype; - - // add the subtype info - $patternSubtypeData = array("patternName" => $patternSubtypeValues["patternSubtypeUC"], "patternSectionSubtype" => true); - if (isset($patternSubtypeValues["patternSubtypeDesc"])) { - $patternSubtypeData["patternDesc"] = $patternSubtypeValues["patternSubtypeDesc"]; - } - if (isset($patternSubtypeValues["patternSubtypeMeta"])) { - $patternSubtypeData["patternDesc"] = $patternSubtypeValues["patternSubtypeDesc"]; - } - $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = $patternSubtypeData; - - // add patterns to $this->patternPartials - foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItem) { - - $patternSectionVanilla = true; - $patternSectionKSS = false; - - // see if this is in KSS - $kssSection = $kss->getSection($patternSubtypeItem["patternPartial"]); - if ($kssSection) { - $patternSectionVanilla = false; - $patternSectionKSS = true; - } - - $patternCode = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"]); - $patternCodeRaw = $patternCode[0]; - $patternCodeEncoded = $patternCode[1]; - $patternLineageExists = (count($this->patternLineages[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; - $patternLineages = $this->patternLineages[$patternSubtypeItem["patternPartial"]]; - $patternLineageRExists = (count($this->patternLineagesR[$patternSubtypeItem["patternPartial"]]) > 0) ? true : false; - $patternLineagesR = $this->patternLineagesR[$patternSubtypeItem["patternPartial"]]; - $patternLineageEExists = ($patternLineageExists || $patternLineageRExists) ? true : false; - - // set-up the mark-up for CSS Rule Saver so it can figure out which rules to save - $patternCSSExists = $this->enableCSS; - $patternCSS = ""; - if ($this->enableCSS) { - $this->cssRuleSaver->loadHTML($patternCodeRaw,false); - $patternCSS = $this->cssRuleSaver->saveRules(); - $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; - } - - $patternPartialData = array("patternSectionVanilla" => $patternSectionVanilla, - "patternSectionKSS" => $patternSectionKSS, - "patternName" => $patternSubtypeItem["patternName"], - "patternLink" => $patternSubtypeItem["patternPath"], - "patternPartial" => $patternSubtypeItem["patternPartial"], - "patternPartialCode" => $patternCodeRaw, - "patternPartialCodeE" => $patternCodeEncoded, - "patternCSSExists" => $patternCSSExists, - "patternCSS" => $patternCSS, - "patternLineageExists" => $patternLineageExists, - "patternLineages" => $patternLineages, - "patternLineageRExists" => $patternLineageRExists, - "patternLineagesR" => $patternLineagesR, - "patternLineageEExists" => $patternLineageEExists); - - if (isset($patternSubtypeItem["patternDesc"])) { - $patternPartialData["patternDesc"] = $patternSubtypeItem["patternDesc"]; - } - - if (isset($patternSubtypeItem["patternMeta"])) { - $patternPartialData["patternMeta"] = $patternSubtypeItem["patternMeta"]; - } - - if ($kssSection) { - $patternPartialData["patternName"] = $kssSection->getTitle(); - $patternPartialData["patternDesc"] = $kssSection->getDescription(); - $modifiers = $kssSection->getModifiers(); - if (count($modifiers) > 0) { - $patternPartialData["patternModifiersExist"] = true; - $patternPartialData["patternModifiers"] = array(); - foreach ($modifiers as $modifier) { - $name = $modifier->getName(); - $class = $modifier->getClassName(); - $desc = $modifier->getDescription(); - $code = ""; - $patternModifierCodeExists = false; - if ($name[0] != ":") { - list($code,$orig) = $this->renderPattern($patternSubtypeItem["patternSrcPath"],$patternSubtypeItem["patternPartial"],array("styleModifier" => $class)); - $patternModifierCodeExists = true; - } - $patternPartialData["patternModifiers"][] = array("patternModifierName" => $name, - "patternModifierDesc" => $desc, - "patternModifierCode" => $code, - "patternModifierCodeExists" => $patternModifierCodeExists); - } - } - } - - $patternPartialData["patternDescExists"] = isset($patternPartialData["patternDesc"]); - - $this->patternPartials[$patternTypeDash."-".$patternSubtypeDash][] = $patternPartialData; - - // set the pattern state - $patternBits = $this->getPatternInfo($patternSubtypeItem["patternPartial"]); - if (($this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"] != "") && (isset($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"]))) { - $this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$patternSubtypeItemKey]["patternState"] = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"]; - } - - } - - } - - } + // modify the pattern mark-up + $markup = $patternStoreData["code"]; + $markupEncoded = htmlentities($markup); + $markupFull = $patternStoreData["header"].$markup.$patternStoreData["footer"]; + $markupEngine = htmlentities(file_get_contents(__DIR__.Config::$options["patternSourceDir"].$pathName.".".Config::$options["patternExtension"])); - // reset the items to take into account removed items affecting the index - if ($arrayReset) { - $this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"] = array_values($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"]); - $arrayReset = false; + // if the pattern directory doesn't exist create it + if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$path)) { + mkdir(__DIR__.Config::$options["patternPublicDir"].$path); } - } else { - - foreach ($patternTypeValues["patternItems"] as $patternSubtypeKey => $patternSubtypeItem) { - // set the pattern state - $patternBits = $this->getPatternInfo($patternSubtypeItem["patternPartial"]); - if ($this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"] != "") { - $this->navItems["patternTypes"][$patternTypeKey]["patternItems"][$patternSubtypeKey]["patternState"] = $this->patternPaths[$patternBits[0]][$patternBits[1]]["patternState"]; - } + // write out the various pattern files + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".html",$markupFull); + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".escaped.html",$markupEncoded); + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".".Config::$options["patternExtension"],$markupEngine); + if (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) { + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".css",htmlentities($this->patternCSS[$p])); } + } } - // add pattern lab's resource to the user-defined files - $templateHelper = new TemplateHelper($this->sp); - $this->patternHead = $templateHelper->patternHead; - $this->patternFoot = $templateHelper->patternFoot; - $this->mainPageHead = $templateHelper->mainPageHead; - $this->mainPageFoot = $templateHelper->mainPageFoot; - - } - - /** - * Get the lineage for a given pattern by parsing it and matching mustache partials - * @param {String} the filename for the pattern to be parsed - * - * @return {Array} a list of patterns - */ - protected function getLineage($filename) { - $data = file_get_contents(__DIR__.$this->sp.$filename.".".$this->patternExtension); - //$data = file_get_contents($filename); - if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-_]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)}}/',$data,$matches)) { - return $matches[2]; - } - return array(); } /** - * Get the lineage for a given pattern by parsing it and matching mustache partials - * @param {String} the filename for the pattern to be parsed - * - * @return {Array} the final set of list items + * Generates the style guide view */ - protected function getListItems($filepath) { - - $listItems = array(); + protected function generateStyleguide() { - // add list item data, makes 'listItems' a reserved word - if (file_exists($filepath)) { + if (!is_dir(Config::$options["publicDir"]."/styleguide/html/")) { - $data = file_get_contents($filepath); - $listItemsJSON = json_decode($data, true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg(str_replace($this->sd."/","",$filepath),$jsonErrorMessage,$data); - } + print "ERROR: the main style guide wasn't written out. make sure public/styleguide exists. can copy core/styleguide\n"; - $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); + } else { - $i = 0; - $k = 1; - $c = count($listItemsJSON)+1; + // grab the partials into a data object for the style guide + $ppExporter = new PatternPartialsExporter(); + $partialsAll = $ppExporter->run(); - while ($k < $c) { - - shuffle($listItemsJSON); - $itemsArray = array(); - //$listItems[$numbers[$k-1]] = array(); - - while ($i < $k) { - $itemsArray[] = $listItemsJSON[$i]; - $i++; - } - - $listItems[$numbers[$k-1]] = $itemsArray; - - $i = 0; - $k++; - - } + // render the style guide + $styleGuideHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); + $styleGuideFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); + $styleGuidePage = $styleGuideHead.Helper::$filesystemLoader->render("viewall",$partialsAll).$styleGuideFoot; + + file_put_contents(Config::$options["publicDir"]."/styleguide/html/styleguide.html",$styleGuidePage); } - return $listItems; - - } - - /** - * Get the directory path for a given pattern or json file by parsing the file path - * @param {String} the filepath for a directory that contained the match - * @param {String} the type of match for the pattern matching, defaults to mustache - * - * @return {String} the directory for the pattern - */ - protected function getPath($filepath,$type = "m") { - $file = ($type == 'm') ? '\.'.$this->patternExtension : '\.json'; - if (preg_match('/\/('.$this->patternTypesRegex.'\/(([A-z0-9-]{1,})\/|)([A-z0-9-]{1,}))'.$file.'$/',$filepath,$matches)) { - return $matches[1]; - } - } - - /** - * Helper function to return the parts of a partial name - * @param {String} the name of the partial - * - * @return {Array} the pattern type and the name of the pattern - */ - private function getPatternInfo($name) { - - $patternBits = explode("-",$name); - - $i = 1; - $k = 2; - $c = count($patternBits); - $patternType = $patternBits[0]; - while (!isset($this->patternPaths[$patternType]) && ($i < $c)) { - $patternType .= "-".$patternBits[$i]; - $i++; - $k++; - } - - $patternBits = explode("-",$name,$k); - $pattern = $patternBits[count($patternBits)-1]; - - return array($patternType, $pattern); - - } - - /** - * Get the name for a given pattern sans any possible digits used for reordering - * @param {String} the pattern based on the filesystem name - * @param {Boolean} whether or not to strip slashes from the pattern name - * - * @return {String} a lower-cased version of the pattern name - */ - protected function getPatternName($pattern, $clean = true) { - $patternBits = explode("-",$pattern,2); - $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; - return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; } /** - * Sets the pattern state on other patterns based on the pattern state for a given partial - * @param {String} the pattern state - * @param {String} the pattern partial + * Generates the view all pages */ - protected function setPatternState($patternState, $patternPartial) { - - // set-up some defaults - $patternState = array_search($patternState, $this->patternStates); + protected function generateViewAllPages() { - // iterate over each of the reverse lineages for a given pattern to update their state - foreach ($this->patternLineagesR[$patternPartial] as $patternCheckInfo) { + // add view all to each list + foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - // run through all of the navitems to find what pattern states match. this feels, and is, overkill - foreach ($this->navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { + if ($patternStoreData["category"] == "patternSubtype") { - if (isset($patternTypeValues["patternTypeItems"])) { + // grab the partials into a data object for the style guide + $ppExporter = new PatternPartialsExporter(); + $partials = $ppExporter->run($patternStoreData["type"],$patternStoreData["name"]); + + if (!empty($partials["partials"])) { - foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { - - // add patterns to $this->patternPartials - foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItem) { - - if ($patternSubtypeItem["patternPartial"] == $patternPartial) { - - if ($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$patternSubtypeItemKey]["patternState"] == "") { - $f = $patternState; - } else { - $patternCheckState = array_search($this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$patternSubtypeItemKey]["patternState"], $this->patternStates); - if ($patternState < $patternCheckState) { - $this->navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$patternSubtypeItemKey]["patternState"] = $patternState; - } - } - - } - - } - - } + $partials["patternPartial"] = "viewall-".$patternStoreData["typeDash"]."-".$patternStoreData["nameDash"]; - } else { + $viewAllHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); + $viewAllFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); + $viewAllPage = $viewAllHead.Helper::$filesystemLoader->render("viewall",$partials).$viewAllFoot; - foreach ($patternTypeValues["patternItems"] as $patternSubtypeKey => $patternSubtypeItem) { - - if ($patternSubtypeItem["patternPartial"] == $patternPartial) { - - if ($this->navItems["patternTypes"][$patternTypeKey]["patternItems"][$patternSubtypeKey]["patternState"] == "") { - $this->navItems["patternTypes"][$patternTypeKey]["patternItems"][$patternSubtypeKey]["patternState"] = $patternState; - } else { - $patternCheckState = array_search($this->navItems["patternTypes"][$patternTypeKey]["patternItems"][$patternSubtypeKey]["patternState"], $this->patternStates); - if ($patternState < $patternCheckState) { - $this->navItems["patternTypes"][$patternTypeKey]["patternItems"][$patternSubtypeKey]["patternState"] = $patternState; - } - } - - } - + // if the pattern directory doesn't exist create it + $patternPath = $patternStoreData["pathDash"]; + if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$patternPath)) { + mkdir(__DIR__.Config::$options["patternPublicDir"].$patternPath); + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); + } else { + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); } } @@ -953,142 +207,9 @@ protected function setPatternState($patternState, $patternPartial) { } - /** - * Write out the time tracking file so the content sync service will work. A holdover - * from how I put together the original AJAX polling set-up. - */ - protected function updateChangeTime() { - - if (is_dir($this->pd."/")) { - file_put_contents($this->pd."/latest-change.txt",time()); - } else { - print "Either the public directory for Pattern Lab doesn't exist or the builder is in the wrong location. Please fix."; - exit; - } - - } - - /** - * Delete patterns and user-created directories and files in public/ - */ - protected function cleanPublic() { - - // make sure patterns exists before trying to clean it - if (is_dir($this->pd."/patterns")) { - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->pd."/patterns/"), \RecursiveIteratorIterator::CHILD_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - // for each file figure out what to do with it - foreach($objects as $name => $object) { - - if ($object->isDir()) { - // if this is a directory remove it - rmdir($name); - } else if ($object->isFile() && ($object->getFilename() != "README")) { - // if this is a file remove it - unlink($name); - } - - } - - } - - // scan source/ & public/ to figure out what directories might need to be cleaned up - $sourceDirs = glob($this->sd."/*",GLOB_ONLYDIR); - $publicDirs = glob($this->pd."/*",GLOB_ONLYDIR); - - // make sure some directories aren't deleted - $ignoreDirs = array("styleguide","snapshots"); - foreach ($ignoreDirs as $ignoreDir) { - $key = array_search($this->pd."/".$ignoreDir,$publicDirs); - if ($key !== false){ - unset($publicDirs[$key]); - } - } - - // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list - foreach ($sourceDirs as $sourceDir) { - $cleanDir = str_replace($this->sd."/","",$sourceDir); - if ($cleanDir[0] == "_") { - $key = array_search($this->pd."/".str_replace("_","",$cleanDir),$publicDirs); - if ($key !== false){ - unset($publicDirs[$key]); - } - } - } - - // for the remaining dirs in public delete them and their files - foreach ($publicDirs as $dir) { - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - if ($object->isDir()) { - rmdir($name); - } else if ($object->isFile()) { - unlink($name); - } - - } - - rmdir($dir); - - } - - } - - /** - * Copies a file from the given source path to the given public path. - * THIS IS NOT FOR PATTERNS - * @param {String} the source file - * @param {String} the public file - */ - protected function moveFile($s,$p) { - if (file_exists($this->sd."/".$s)) { - copy($this->sd."/".$s,$this->pd."/".$p); - } - } - - /** - * Moves static files that aren't directly related to Pattern Lab - * @param {String} file name to be moved - * @param {String} copy for the message to be printed out - * @param {String} part of the file name to be found for replacement - * @param {String} the replacement - */ - protected function moveStaticFile($fileName,$copy = "", $find = "", $replace = "") { - $this->moveFile($fileName,str_replace($find, $replace, $fileName)); - $this->updateChangeTime(); - if ($copy != "") { - print $fileName." ".$copy."...\n"; - } - } - - /** - * Check to see if a given filename is in a directory that should be ignored - * @param {String} file name to be checked - * - * @return {Boolean} whether the directory should be ignored - */ - protected function ignoreDir($fileName) { - foreach($this->id as $dir) { - $pos = strpos(DIRECTORY_SEPARATOR.$fileName,DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR); - if ($pos !== false) { - return true; - } - } - return false; - } - /** * Loads the CSS from source/css/ into CSS Rule Saver to be used for code view + * Will eventually get pushed elsewhere */ protected function initializeCSSRuleSaver() { @@ -1097,56 +218,10 @@ protected function initializeCSSRuleSaver() { $this->cssRuleSaver = new \CSSRuleSaver\CSSRuleSaver; - foreach(glob($this->sd."/css/*.css") as $filename) { + foreach(glob(Config::$options["sourceDir"]."/css/*.css") as $filename) { $this->cssRuleSaver->loadCSS($filename); } } - /** - * Print out the data var. For debugging purposes - * - * @return {String} the formatted version of the d object - */ - public function printData() { - print_r($this->d); - } - - /** - * Go through data and replace any values that match items from the link.array - * @param {String} an entry from one of the list-based config entries - * - * @return {String} trimmed version of the given $v var - */ - public function compareReplace(&$value) { - if (is_string($value)) { - $valueCheck = strtolower($value); - $valueThin = str_replace("link.","",$valueCheck); - if ((strpos($valueCheck, 'link.') !== false) && array_key_exists($valueThin,$this->d["link"])) { - $value = $this->d["link"][$valueThin]; - } - } - - } - - /** - * Trim a given string. Used in the array_walk() function in __construct as a sanity check - * @param {String} an entry from one of the list-based config entries - * - * @return {String} trimmed version of the given $v var - */ - public function trim(&$v) { - $v = trim($v); - } - - /** - * Lowercase the given string. Used in the array_walk() function in __construct as a sanity check - * @param {String} an entry from one of the list-based config entries - * - * @return {String} lowercased version of the given $v var - */ - public function strtolower(&$v) { - $v = strtolower($v); - } - } From 91feae4ef53d35ed8b8466fc45bb94565b4bb16b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:47:48 -0400 Subject: [PATCH 115/166] removing copy --- core/lib/PatternLab/Console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php index f75739585..d30e4a33b 100644 --- a/core/lib/PatternLab/Console.php +++ b/core/lib/PatternLab/Console.php @@ -1,7 +1,7 @@ Date: Fri, 30 May 2014 22:47:57 -0400 Subject: [PATCH 116/166] updating copy --- core/lib/PatternLab/Generator.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index 60d65cb30..c3752bc4d 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -1,13 +1,12 @@ Date: Fri, 30 May 2014 22:48:07 -0400 Subject: [PATCH 117/166] including classes used in the generator --- core/lib/PatternLab/Generator.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index c3752bc4d..27527f933 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -12,6 +12,13 @@ namespace PatternLab; +use \PatternLab\Builder; +use \PatternLab\Config; +use \PatternLab\Data; +use \PatternLab\FileUtil; +use \PatternLab\PatternData; +use \PatternLab\Util; + class Generator extends Builder { /** From cae0ccd607203aa59c0561006e774180da7e2edf Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:48:21 -0400 Subject: [PATCH 118/166] fixing up to reflect changes to builder --- core/lib/PatternLab/Generator.php | 48 +++++++++++++++++-------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index 27527f933..999272054 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -47,7 +47,9 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster $starttime = $mtime; } - $this->noCacheBuster = $noCacheBuster; + if ($noCacheBuster) { + Config::$options["cacheBuster"] = 0; + } if ($enableCSS) { @@ -61,38 +63,40 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster } // gather up all of the data to be used in patterns - $this->gatherData(); + Data::gather(); // gather all of the various pattern info - $this->gatherPatternInfo(); + PatternData::gather(); // clean the public directory to remove old files - if (($this->cleanPublic == "true") && $moveStatic) { - $this->cleanPublic(); + if ((Config::$options["cleanPublic"] == "true") && $moveStatic) { + Util::cleanPublic(); } + // render out the index and style guide + $this->generateIndex(); + $this->generateStyleguide(); + $this->generateViewAllPages(); + // render out the patterns and move them to public/patterns $this->generatePatterns(); - // render out the index and style guide - $this->generateMainPages(); - // make sure data exists - if (!is_dir($this->pd."/data")) { - mkdir($this->pd."/data"); + if (!is_dir(__DIR__.Config::$options["patternPublicDir"]."/data")) { + mkdir(__DIR__.Config::$options["patternPublicDir"]."/data"); } // iterate over the data files and regenerate the entire site if they've changed - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); foreach($objects as $name => $object) { - $fileName = str_replace($this->sd."/_data".DIRECTORY_SEPARATOR,"",$name); + $fileName = str_replace(Config::$options["sourceDir"]."/_data".DIRECTORY_SEPARATOR,"",$name); if (($fileName[0] != "_") && $object->isFile()) { - $this->moveStaticFile("_data/".$fileName,"","_data","data"); + FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); } } @@ -101,7 +105,7 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster if ($moveStatic) { // iterate over all of the other files in the source directory - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd."/"), \RecursiveIteratorIterator::SELF_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); @@ -109,20 +113,20 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster foreach($objects as $name => $object) { // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace($this->sd.DIRECTORY_SEPARATOR,"",$name); - if (($fileName[0] != "_") && (!in_array($object->getExtension(),$this->ie)) && (!in_array($object->getFilename(),$this->id))) { + $fileName = str_replace(Config::$options["sourceDir"].DIRECTORY_SEPARATOR,"",$name); + if (($fileName[0] != "_") && (!in_array($object->getExtension(),Config::$options["ie"])) && (!in_array($object->getFilename(),Config::$options["id"]))) { // catch directories that have the ignored dir in their path - $ignoreDir = $this->ignoreDir($fileName); + $ignoreDir = FileUtil::ignoreDir($fileName); // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !is_dir($this->pd."/".$fileName)) { - mkdir($this->pd."/".$fileName); + if (!$ignoreDir && $object->isDir() && !is_dir(__DIR__.Config::$options["patternPublicDir"]."/".$fileName)) { + mkdir(__DIR__.Config::$options["patternPublicDir"]."/".$fileName); } // check to see if it's a new file or a file that has changed - if (!$ignoreDir && $object->isFile() && (!file_exists($this->pd."/".$fileName))) { - $this->moveStaticFile($fileName); + if (!$ignoreDir && $object->isFile() && (!file_exists(__DIR__.Config::$options["patternPublicDir"]."/".$fileName))) { + FileUtil::moveStaticFile($fileName); } } @@ -132,7 +136,7 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster } // update the change time so the auto-reload will fire (doesn't work for the index and style guide) - $this->updateChangeTime(); + Util::updateChangeTime(); print "your site has been generated...\n"; From 1e5b72d4fcacadafeb361c02ddd2bc74dbdfbd0e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:48:56 -0400 Subject: [PATCH 119/166] updating copy --- core/lib/PatternLab/JSON.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/lib/PatternLab/JSON.php b/core/lib/PatternLab/JSON.php index b824d3e1b..b0bd0553b 100644 --- a/core/lib/PatternLab/JSON.php +++ b/core/lib/PatternLab/JSON.php @@ -1,11 +1,13 @@ Date: Fri, 30 May 2014 22:49:10 -0400 Subject: [PATCH 120/166] updating copy --- core/lib/PatternLab/Migrator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Migrator.php b/core/lib/PatternLab/Migrator.php index 7d61b847e..2e75f7fa9 100644 --- a/core/lib/PatternLab/Migrator.php +++ b/core/lib/PatternLab/Migrator.php @@ -1,7 +1,7 @@ Date: Fri, 30 May 2014 22:49:36 -0400 Subject: [PATCH 121/166] updating copy --- core/lib/PatternLab/Snapshot.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php index 51dea8a5d..dbfa379eb 100644 --- a/core/lib/PatternLab/Snapshot.php +++ b/core/lib/PatternLab/Snapshot.php @@ -1,7 +1,7 @@ Date: Fri, 30 May 2014 22:49:47 -0400 Subject: [PATCH 122/166] adding classes used in this one --- core/lib/PatternLab/Snapshot.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php index dbfa379eb..bb9591072 100644 --- a/core/lib/PatternLab/Snapshot.php +++ b/core/lib/PatternLab/Snapshot.php @@ -12,6 +12,10 @@ namespace PatternLab; +use \PatternLab\Config; +use \PatternLab\Snapshot\FilterIterator; +use \PatternLab\Template\Helper; + class Snapshot { /** From c4419b678ad8ee073a86b6079c8048bd42e5cc5f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:50:02 -0400 Subject: [PATCH 123/166] referencing the new classes in the code --- core/lib/PatternLab/Snapshot.php | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php index bb9591072..657520610 100644 --- a/core/lib/PatternLab/Snapshot.php +++ b/core/lib/PatternLab/Snapshot.php @@ -19,16 +19,17 @@ class Snapshot { /** - * Set-up a default var + * Set-up default vars */ public function __construct($config) { - $this->publicDir = __DIR__."/../../../".$config["publicDir"]; - $this->sourceDir = "/../../../".$config["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; + $this->publicDir = __DIR__."/../../../".Config::$options["sourceDir"]; + $this->sourceDir = "/../../../".Config::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; $this->snapshotsBase = $this->publicDir."/snapshots/"; } /** - * Get the arguments that have been passed to the script via the commmand line + * Take a snap shot of the public dir + * @param {String} the directory where we wil be writing the snapshot */ public function takeSnapshot($dir) { @@ -80,7 +81,7 @@ public function takeSnapshot($dir) { // iterate over all of the other files in the source directory $directoryIterator = new \RecursiveDirectoryIterator($this->publicDir); - $filteredIterator = new SnapshotFilterIterator($directoryIterator); + $filteredIterator = new FilterIterator($directoryIterator); $objects = new \RecursiveIteratorIterator($filteredIterator, \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped @@ -123,17 +124,14 @@ public function takeSnapshot($dir) { $d = array("html" => $html); - $templateLoader = new TemplateLoader(); - $templateHelper = new TemplateHelper($this->sourceDir); + TemplateHelper::setup(); // render the viewall template - $v = $templateLoader->vanilla(); - $h = $v->render($templateHelper->mainPageHead); - $f = $v->render($templateHelper->mainPageFoot); + $h = Helper::$htmlLoader->render(Helper::$mainPageHead); + $f = Helper::$htmlLoader->render(Helper::$mainPageFoot); // render the snapshot page - $m = $templateLoader->fileSystem(); - $r = $m->render('snapshot', $d); + $r = Helper::$filesystemLoader->render("snapshot", $d); $r = $h.$r.$f; file_put_contents($this->snapshotsBase."index.html",$r); From 0ae0992a1d6d57d12b6a9e2be7194cbb4a0929dc Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:51:42 -0400 Subject: [PATCH 124/166] referencing new classes & config --- core/lib/PatternLab/StarterKit.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core/lib/PatternLab/StarterKit.php b/core/lib/PatternLab/StarterKit.php index 9d2bfa7c3..568dd4e5c 100644 --- a/core/lib/PatternLab/StarterKit.php +++ b/core/lib/PatternLab/StarterKit.php @@ -1,7 +1,7 @@ sourceDir = __DIR__."/../../../".$config["sourceDir"]; - if (!is_dir($this->sourceDir)) { + public function __construct() { + if (!is_dir(Config::$options["sourceDir"])) { print "Check to make sure your source directory is configured properly...\n"; exit; } @@ -60,7 +62,7 @@ public function fetch($starterKit) { // see if the source directory is empty $emptyDir = true; - $objects = new \DirectoryIterator($this->sourceDir); + $objects = new \DirectoryIterator(Config::$options["sourceDir"]); foreach ($objects as $object) { if (!$object->isDot() && ($object->getFilename() != "README") && ($object->getFilename() != ".DS_Store")) { $emptyDir = false; @@ -79,7 +81,7 @@ public function fetch($starterKit) { print "nuking everything in source/...\n"; - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sourceDir), \RecursiveIteratorIterator::CHILD_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]), \RecursiveIteratorIterator::CHILD_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); @@ -103,10 +105,10 @@ public function fetch($starterKit) { } // extract - $zippy = \Alchemy\Zippy\Zippy::load(); + $zippy = Zippy::load(); $zipAdapter = $zippy->getAdapterFor('tar.gz'); $archiveZip = $zipAdapter->open($tempFile); - $archiveZip = $archiveZip->extract($this->sourceDir); + $archiveZip = $archiveZip->extract(Config::$options["sourceDir"]); // remove the temp file unlink($tempFile); From de79951dd0cad5aa784fca6e7236450c7587155b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:51:54 -0400 Subject: [PATCH 125/166] adding util class --- core/lib/PatternLab/Util.php | 147 +++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 core/lib/PatternLab/Util.php diff --git a/core/lib/PatternLab/Util.php b/core/lib/PatternLab/Util.php new file mode 100644 index 000000000..6b74856a4 --- /dev/null +++ b/core/lib/PatternLab/Util.php @@ -0,0 +1,147 @@ +setFlags(\FilesystemIterator::SKIP_DOTS); + + // for each file figure out what to do with it + foreach($objects as $name => $object) { + + if ($object->isDir()) { + // if this is a directory remove it + rmdir($name); + } else if ($object->isFile() && ($object->getFilename() != "README")) { + // if this is a file remove it + unlink($name); + } + + } + + } + + // scan source/ & public/ to figure out what directories might need to be cleaned up + $sourceDirs = glob(Config::$options["sourceDir"]."/*",GLOB_ONLYDIR); + $publicDirs = glob(Config::$options["publicDir"]."/*",GLOB_ONLYDIR); + + // make sure some directories aren't deleted + $ignoreDirs = array("styleguide","snapshots"); + foreach ($ignoreDirs as $ignoreDir) { + $key = array_search(Config::$options["publicDir"]."/".$ignoreDir,$publicDirs); + if ($key !== false){ + unset($publicDirs[$key]); + } + } + + // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list + foreach ($sourceDirs as $sourceDir) { + $cleanDir = str_replace(Config::$options["sourceDir"]."/","",$sourceDir); + if ($cleanDir[0] == "_") { + $key = array_search(Config::$options["publicDir"]."/".str_replace("_","",$cleanDir),$publicDirs); + if ($key !== false){ + unset($publicDirs[$key]); + } + } + } + + // for the remaining dirs in public delete them and their files + foreach ($publicDirs as $dir) { + + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST); + + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); + + foreach($objects as $name => $object) { + + if ($object->isDir()) { + rmdir($name); + } else if ($object->isFile()) { + unlink($name); + } + + } + + rmdir($dir); + + } + + } + + /** + * Go through data and replace any values that match items from the link.array + * @param {String} an entry from one of the list-based config entries + * + * @return {String} trimmed version of the given $v var + */ + public static function compareReplace(&$value) { + if (is_string($value)) { + $valueCheck = strtolower($value); + $valueThin = str_replace("link.","",$valueCheck); + if ((strpos($valueCheck, 'link.') !== false) && array_key_exists($valueThin,Data::$store["link"])) { + $value = Data::$store["link"][$valueThin]; + } + } + + } + + /** + * Lowercase the given string. Used in the array_walk() function in __construct as a sanity check + * @param {String} an entry from one of the list-based config entries + * + * @return {String} lowercased version of the given $v var + */ + public static function strtolower(&$v) { + $v = strtolower($v); + } + + /** + * Trim a given string. Used in the array_walk() function in __construct as a sanity check + * @param {String} an entry from one of the list-based config entries + * + * @return {String} trimmed version of the given $v var + */ + public static function trim(&$v) { + $v = trim($v); + } + + /** + * Write out the time tracking file so the content sync service will work. A holdover + * from how I put together the original AJAX polling set-up. + */ + public static function updateChangeTime() { + + if (is_dir(Config::$options["publicDir"]."/")) { + file_put_contents(Config::$options["publicDir"]."/latest-change.txt",time()); + } else { + print "Either the public directory for Pattern Lab doesn't exist or the builder is in the wrong location. Please fix."; + exit; + } + + } + +} From 808527d33eaebb3264b9da2303e114f2d86c6063 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:52:22 -0400 Subject: [PATCH 126/166] adding render class for simple rendering of patterns --- core/lib/PatternLab/Render.php | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 core/lib/PatternLab/Render.php diff --git a/core/lib/PatternLab/Render.php b/core/lib/PatternLab/Render.php new file mode 100644 index 000000000..c7140a07d --- /dev/null +++ b/core/lib/PatternLab/Render.php @@ -0,0 +1,63 @@ +render($filePath,$data); + return $pattern; + + } + + /** + * Renders a given mark-up (header) using Mustache and incorporating the provided data + * @param {String} the mark-up to be rendered + * @param {Array} the data related to the pattern + * + * @return {String} the mark-up as rendered by Mustache + */ + public static function Header($html,$data) { + + $header = Helper::$htmlLoader->render($html,$data); + return $header; + + } + + /** + * Renders a given mark-up (footer) using Mustache and incorporating the provided data + * @param {String} the mark-up to be rendered + * @param {Array} the data related to the pattern + * + * @return {String} the mark-up as rendered by Mustache + */ + public static function Footer($html,$data) { + + $footer = Helper::$htmlLoader->render($html,$data); + return $footer; + + } + +} From 88eba30fd20674f7643d38ad94c9fbf67045c5d3 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:52:33 -0400 Subject: [PATCH 127/166] adding new data --- .../pattern-header-footer/footer-pattern.html | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/templates/pattern-header-footer/footer-pattern.html b/core/templates/pattern-header-footer/footer-pattern.html index 16a207954..772e94e13 100644 --- a/core/templates/pattern-header-footer/footer-pattern.html +++ b/core/templates/pattern-header-footer/footer-pattern.html @@ -1,14 +1,15 @@ \ No newline at end of file From b0ae3758bc828392921c8be373b763a32e28b286 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Fri, 30 May 2014 22:52:46 -0400 Subject: [PATCH 128/166] reflecting the changes to builder --- core/lib/PatternLab/Watcher.php | 54 +++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index e73d73da1..707237db9 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -1,7 +1,7 @@ noCacheBuster = $noCacheBuster; + if ($noCacheBuster) { + Config::$options["cacheBuster"] = 0; + } $c = false; // track that one loop through the pattern file listing has completed $o = new \stdClass(); // create an object to hold the properties @@ -59,7 +68,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals $cp = clone $o->patterns; // iterate over the patterns & related data and regenerate the entire site if they've changed - $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd."/_patterns/"), \RecursiveIteratorIterator::SELF_FIRST); + $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_patterns/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); @@ -67,7 +76,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals foreach($patternObjects as $name => $object) { // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace($this->sd."/_patterns".DIRECTORY_SEPARATOR,"",$name); + $fileName = str_replace(Config::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR,"",$name); $fileNameClean = str_replace(DIRECTORY_SEPARATOR."_",DIRECTORY_SEPARATOR,$fileName); if ($object->isFile() && (($object->getExtension() == "mustache") || ($object->getExtension() == "json") || ($object->getExtension() == "md"))) { @@ -133,26 +142,26 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals } // iterate over the data files and regenerate the entire site if they've changed - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); foreach($objects as $name => $object) { - $fileName = str_replace($this->sd."/_data".DIRECTORY_SEPARATOR,"",$name); + $fileName = str_replace(Config::$options["sourceDir"]."/_data".DIRECTORY_SEPARATOR,"",$name); $mt = $object->getMTime(); if (!isset($o->$fileName)) { $o->$fileName = $mt; if (($fileName[0] != "_") && $object->isFile()) { - $this->moveStaticFile("_data/".$fileName,"","_data","data"); + FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); } } else if ($o->$fileName != $mt) { $o->$fileName = $mt; $this->updateSite($fileName,"changed"); if (($fileName[0] != "_") && $object->isFile()) { - $this->moveStaticFile("_data/".$fileName,"","_data","data"); + FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); } } @@ -161,7 +170,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals // iterate over all of the other files in the source directory and move them if their modified time has changed if ($moveStatic) { - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->sd."/"), \RecursiveIteratorIterator::SELF_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); @@ -169,15 +178,15 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals foreach($objects as $name => $object) { // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace($this->sd.DIRECTORY_SEPARATOR,"",$name); + $fileName = str_replace(Config::$options["sourceDir"].DIRECTORY_SEPARATOR,"",$name); if (($fileName[0] != "_") && (!in_array($object->getExtension(),$this->ie)) && (!in_array($object->getFilename(),$this->id))) { // catch directories that have the ignored dir in their path $ignoreDir = $this->ignoreDir($fileName); // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir($this->pd."/".$fileName)) { - mkdir($this->pd."/".$fileName); + if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir(Config::$options["patternPublicDir"]."/".$fileName)) { + mkdir(Config::$options["patternPublicDir"]."/".$fileName); $o->$fileName = "dir created"; // placeholder print $fileName."/ directory was created...\n"; } @@ -186,15 +195,15 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals if (file_exists($name)) { $mt = $object->getMTime(); - if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists($this->pd."/".$fileName)) { + if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists(Config::$options["patternPublicDir"]."/".$fileName)) { $o->$fileName = $mt; - $this->moveStaticFile($fileName,"added"); + FileUtil::moveStaticFile($fileName,"added"); if ($object->getExtension() == "css") { $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons } } else if (!$ignoreDir && $object->isFile() && isset($o->$fileName) && ($o->$fileName != $mt)) { $o->$fileName = $mt; - $this->moveStaticFile($fileName,"changed"); + FileUtil::moveStaticFile($fileName,"changed"); if ($object->getExtension() == "css") { $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons } @@ -245,12 +254,17 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals * @return {String} the final message */ private function updateSite($fileName,$message,$verbose = true) { - $this->gatherData(); - $this->gatherPatternInfo(); - $this->generatePatterns(); + + Data::gather(); + PatternData::gather(); + + $this->generateIndex(); + $this->generateStyleguide(); $this->generateViewAllPages(); - $this->updateChangeTime(); - $this->generateMainPages(); + $this->generatePatterns(); + + Util::updateChangeTime(); + if ($verbose) { if ($message == "added") { print $fileName." was added to Pattern Lab. Reload the website to see this change in the navigation...\n"; From ce437529b4f282f90d8d07e9049d82f4ac4bcead Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 21:34:15 -0400 Subject: [PATCH 129/166] correctly referencing publicDir --- core/lib/PatternLab/FileUtil.php | 2 +- core/lib/PatternLab/Generator.php | 7 ++++--- core/lib/PatternLab/Watcher.php | 9 +++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/core/lib/PatternLab/FileUtil.php b/core/lib/PatternLab/FileUtil.php index ead066bd3..7ef13d61b 100644 --- a/core/lib/PatternLab/FileUtil.php +++ b/core/lib/PatternLab/FileUtil.php @@ -24,7 +24,7 @@ class FileUtil { */ protected static function moveFile($s,$p) { if (file_exists(Config::$options["sourceDir"]."/".$s)) { - copy(Config::$options["sourceDir"]."/".$s,__DIR__.Config::$options["patternPublicDir"]."/".$p); + copy(Config::$options["sourceDir"]."/".$s,Config::$options["publicDir"]."/".$p); } } diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index 999272054..f058e2b4d 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -114,18 +114,19 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored $fileName = str_replace(Config::$options["sourceDir"].DIRECTORY_SEPARATOR,"",$name); + if (($fileName[0] != "_") && (!in_array($object->getExtension(),Config::$options["ie"])) && (!in_array($object->getFilename(),Config::$options["id"]))) { // catch directories that have the ignored dir in their path $ignoreDir = FileUtil::ignoreDir($fileName); // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !is_dir(__DIR__.Config::$options["patternPublicDir"]."/".$fileName)) { - mkdir(__DIR__.Config::$options["patternPublicDir"]."/".$fileName); + if (!$ignoreDir && $object->isDir() && !is_dir(Config::$options["publicDir"]."/".$fileName)) { + mkdir(Config::$options["publicDir"]."/".$fileName); } // check to see if it's a new file or a file that has changed - if (!$ignoreDir && $object->isFile() && (!file_exists(__DIR__.Config::$options["patternPublicDir"]."/".$fileName))) { + if (!$ignoreDir && $object->isFile() && (!file_exists(Config::$options["publicDir"]."/".$fileName))) { FileUtil::moveStaticFile($fileName); } diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index 707237db9..65cae3c69 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -185,8 +185,8 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals $ignoreDir = $this->ignoreDir($fileName); // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir(Config::$options["patternPublicDir"]."/".$fileName)) { - mkdir(Config::$options["patternPublicDir"]."/".$fileName); + if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir(Config::$options["publicDir"]."/".$fileName)) { + mkdir(Config::$options["publicDir"]."/".$fileName); $o->$fileName = "dir created"; // placeholder print $fileName."/ directory was created...\n"; } @@ -195,7 +195,7 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals if (file_exists($name)) { $mt = $object->getMTime(); - if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists(Config::$options["patternPublicDir"]."/".$fileName)) { + if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists(Config::$options["publicDir"]."/".$fileName)) { $o->$fileName = $mt; FileUtil::moveStaticFile($fileName,"added"); if ($object->getExtension() == "css") { @@ -225,9 +225,6 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals $c = true; // taking out the garbage. basically killing mustache after each run. - unset($this->pl); - unset($this->msf); - unset($this->mv); if (gc_enabled()) gc_collect_cycles(); // output anything the reload server might send our way From 2de36c9416edbe5d1841eabb08312c4c95f2294e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 21:35:00 -0400 Subject: [PATCH 130/166] properly referencing vars for showing the viewall views --- .../PatternData/Exporters/PatternPartialsExporter.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php index 144ff3b98..c02a73e64 100644 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php @@ -46,6 +46,7 @@ public function run($type = "", $subtype = "") { $patternPartialData["patternName"] = ucwords($patternStoreData["nameClean"]); $patternPartialData["patternLink"] = $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"; $patternPartialData["patternPartial"] = $patternStoreData["partial"]; + $patternPartialData["patternPartialCode"] = $patternStoreData["code"]; $patternPartialData["patternLineageExists"] = isset($patternStoreData["lineages"]); $patternPartialData["patternLineages"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); @@ -54,10 +55,10 @@ public function run($type = "", $subtype = "") { $patternPartialData["patternLineageEExists"] = (isset($patternStoreData["lineages"]) || isset($patternStoreData["lineagesR"])); $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]); - $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; + $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; $patternPartialData["patternModifiersExists"] = isset($patternStoreData["modifiers"]); - $patternPartialData["patternModifiersExists"] = isset($patternStoreData["modifiers"]) ? $patternStoreData["modifiers"] : array(); + $patternPartialData["patternModifiers"] = isset($patternStoreData["modifiers"]) ? $patternStoreData["modifiers"] : array(); $patternPartialData["patternCSSExists"] = Config::$options["enableCSS"]; From 09c069cb155050081dd317c7c2d46dff1d1cbf6e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 21:35:28 -0400 Subject: [PATCH 131/166] setting vars to empty array so they don't throw errors when included --- core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php b/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php index f78ebbd04..6530e304e 100644 --- a/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php +++ b/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php @@ -41,8 +41,8 @@ public function run() { $patternFooterData = array("patternFooterData" => array()); //$patternFooterData["patternFooterData"]["cssEnabled"] = (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) ? "true" : "false"; - $patternFooterData["patternFooterData"]["lineage"] = isset($patternStoreData["lineages"]) ? json_encode($patternStoreData["lineages"]) : ""; - $patternFooterData["patternFooterData"]["lineageR"] = isset($patternStoreData["lineagesR"]) ? json_encode($patternStoreData["lineagesR"]) : ""; + $patternFooterData["patternFooterData"]["lineage"] = isset($patternStoreData["lineages"]) ? json_encode($patternStoreData["lineages"]) : "[]"; + $patternFooterData["patternFooterData"]["lineageR"] = isset($patternStoreData["lineagesR"]) ? json_encode($patternStoreData["lineagesR"]) : "[]"; $patternFooterData["patternFooterData"]["patternBreadcrumb"] = $patternStoreData["breadcrumb"]; $patternFooterData["patternFooterData"]["patternDesc"] = (isset($patternStoreData["desc"])) ? $patternStoreData["desc"] : ""; $patternFooterData["patternFooterData"]["patternExtension"] = Config::$options["patternExtension"]; From 460e4b83581ee14df84797687744c0ab6fb2b689 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 22:59:19 -0400 Subject: [PATCH 132/166] properly included PL classes --- .../PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php index cbb6d2be2..008cd24b1 100644 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -13,9 +13,11 @@ namespace PatternLab\PatternData\Helpers\Plugins; use \PatternLab\Config; +use \PatternLab\Data; use \PatternLab\Parsers\Plugins\KSS; use \PatternLab\PatternData; use \PatternLab\PatternEngine; +use \PatternLab\Render; class KSSHelperPlugin extends \PatternLab\PatternData\Helper { From a1670e01e3c034f9537cb3927b801cecfd1fbd91 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 22:59:44 -0400 Subject: [PATCH 133/166] removing an unnecessary var for getpatternspecific data --- .../PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php index 008cd24b1..bacd76f56 100644 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -63,7 +63,7 @@ public function run() { if ($name[0] != ":") { - $data = Data::getPatternSpecificData($patternStoreKey,$patternFooterData); + $data = Data::getPatternSpecificData($patternStoreKey); $data = array_merge($data,array("styleModifier" => $class)); $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; $code = Render::Pattern($srcPath,$patternStoreKey,$data); From ef772b434e5b08feee3acb9bc03b98588c942503 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 22:59:56 -0400 Subject: [PATCH 134/166] proper vars for render --- .../PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php index bacd76f56..2c68f5c83 100644 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -66,7 +66,7 @@ public function run() { $data = Data::getPatternSpecificData($patternStoreKey); $data = array_merge($data,array("styleModifier" => $class)); $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; - $code = Render::Pattern($srcPath,$patternStoreKey,$data); + $code = Render::Pattern($srcPath,$data); $modifierCodeExists = true; } From 5aff44e4750592602a221363ad3740e2749f3bc0 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 23:00:02 -0400 Subject: [PATCH 135/166] copy fix --- .../PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php index 2c68f5c83..9496701af 100644 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -65,6 +65,7 @@ public function run() { $data = Data::getPatternSpecificData($patternStoreKey); $data = array_merge($data,array("styleModifier" => $class)); + $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; $code = Render::Pattern($srcPath,$data); From ae985b5c58e18407d74bd49defbe8f188a2f8736 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 2 Jun 2014 23:00:17 -0400 Subject: [PATCH 136/166] properly naming vars so the KSS shows up --- .../PatternData/Exporters/PatternPartialsExporter.php | 2 +- core/templates/partials/patternSection.mustache | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php index c02a73e64..e64f5242e 100644 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php @@ -57,7 +57,7 @@ public function run($type = "", $subtype = "") { $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]); $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; - $patternPartialData["patternModifiersExists"] = isset($patternStoreData["modifiers"]); + $patternPartialData["patternModifiersExist"] = isset($patternStoreData["modifiers"]); $patternPartialData["patternModifiers"] = isset($patternStoreData["modifiers"]) ? $patternStoreData["modifiers"] : array(); $patternPartialData["patternCSSExists"] = Config::$options["enableCSS"]; diff --git a/core/templates/partials/patternSection.mustache b/core/templates/partials/patternSection.mustache index 42cc624a2..b7033459e 100644 --- a/core/templates/partials/patternSection.mustache +++ b/core/templates/partials/patternSection.mustache @@ -11,7 +11,7 @@ {{# patternModifiersExist }}
      {{# patternModifiers }} -
    • {{ patternModifierName }}: {{ patternModifierDesc }}
    • +
    • {{ modifierName }}: {{ modifierDesc }}
    • {{/ patternModifiers }}
    {{/ patternModifiersExist }} @@ -22,12 +22,12 @@
    {{# patternModifiersExist }} {{# patternModifiers }} - {{# patternModifierCodeExists }} + {{# modifierCodeExists }}
    - {{ patternModifierName }} - {{{ patternModifierCode }}} + {{ modifierName }} + {{{ modifierCode }}}
    - {{/ patternModifierCodeExists }} + {{/ modifierCodeExists }} {{/ patternModifiers }} {{/ patternModifiersExist }}
    From e36548aa117c56a5b7d61f43201e1bab9af3c52f Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:00:25 -0400 Subject: [PATCH 137/166] only need to render the header for the viewall page once --- core/lib/PatternLab/Builder.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 6c0ec60aa..f310ab545 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -173,6 +173,9 @@ protected function generateStyleguide() { */ protected function generateViewAllPages() { + $viewAllHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); + $viewAllFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); + // add view all to each list foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { From 2ce6e3e2d5bbb5905cf78614554ff1f5909cc478 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:00:51 -0400 Subject: [PATCH 138/166] function to check if a pattern type has a subtype --- core/lib/PatternLab/PatternData.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/lib/PatternLab/PatternData.php b/core/lib/PatternLab/PatternData.php index b22642246..c9faa16ee 100644 --- a/core/lib/PatternLab/PatternData.php +++ b/core/lib/PatternLab/PatternData.php @@ -37,6 +37,21 @@ class PatternData { public static $rules = array(); public static $dirSep = DIRECTORY_SEPARATOR; + /** + * Check to see if the given pattern type has a pattern subtype associated with it + * @param {String} the name of the pattern + * + * @return {Boolean} if it was found or not + */ + public static function hasPatternSubtype($patternType) { + foreach (self::$store as $patternStoreKey => $patternStoreData) { + if (($patternStoreData["category"] == "patternSubtype") && ($patternStoreData["typeDash"] == $patternType)) { + return true; + } + } + return false; + } + /** * Gather all of the information related to the patterns */ From 72519257c06bd88fa4b4a381639cbecbcc851149 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:01:23 -0400 Subject: [PATCH 139/166] adding pathname and path dash props to pattern type --- .../PatternData/Rules/PatternTypeRule.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php b/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php index dfe27b5ad..240f25649 100644 --- a/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php +++ b/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php @@ -31,13 +31,19 @@ public function __construct($options) { public function run($depth, $ext, $path, $pathName, $name) { + // load default vars + $dirSep = PatternData::$dirSep; + // set-up the names - $patternType = $name; // 00-atoms - $patternTypeDash = $this->getPatternName($name,false); // atoms - $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) + $patternType = $name; // 00-atoms + $patternTypeDash = $this->getPatternName($name,false); // atoms + $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) + + $patternTypePath = $pathName; // 00-atoms/02-blocks + $patternTypePathDash = str_replace($dirSep,"-",$patternTypePath); // 00-atoms-02-blocks (file path) // create a key for the data store - $patternStoreKey = $patternTypeDash."-pltype"; + $patternStoreKey = $patternTypeDash."-pltype"; // add a new patternType to the nav PatternData::$store[$patternStoreKey] = array("category" => "patternType", @@ -47,8 +53,9 @@ public function run($depth, $ext, $path, $pathName, $name) { "depth" => $depth, "ext" => $ext, "path" => $path, - "pathName" => $pathName, - "isDir" => $this->isDirProp , + "pathName" => $patternTypePath, + "pathDash" => $patternTypePathDash, + "isDir" => $this->isDirProp, "isFile" => $this->isFileProp); // starting a new set of pattern types. it might not have any pattern subtypes From 5e2a91a00bbb43ed8c9f5e7b78f533af19e9cda3 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:02:03 -0400 Subject: [PATCH 140/166] simple js fix --- core/templates/partials/viewAllPaths.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/partials/viewAllPaths.mustache b/core/templates/partials/viewAllPaths.mustache index b0e044d97..885dc59e1 100644 --- a/core/templates/partials/viewAllPaths.mustache +++ b/core/templates/partials/viewAllPaths.mustache @@ -1,3 +1,3 @@ \ No newline at end of file From e2570f5c8bc77d41d40eff0141ad8015b4a653c6 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:02:27 -0400 Subject: [PATCH 141/166] adding viewall support for pattern types with subtypes --- core/lib/PatternLab/Builder.php | 25 +++++++++++++++++-- .../Exporters/NavItemsExporter.php | 11 ++++++++ .../Exporters/PatternPartialsExporter.php | 2 +- .../Exporters/ViewAllPathsExporter.php | 6 +++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index f310ab545..a22286136 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -189,8 +189,29 @@ protected function generateViewAllPages() { $partials["patternPartial"] = "viewall-".$patternStoreData["typeDash"]."-".$patternStoreData["nameDash"]; - $viewAllHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); - $viewAllFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); + $viewAllPage = $viewAllHead.Helper::$filesystemLoader->render("viewall",$partials).$viewAllFoot; + + // if the pattern directory doesn't exist create it + $patternPath = $patternStoreData["pathDash"]; + if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$patternPath)) { + mkdir(__DIR__.Config::$options["patternPublicDir"].$patternPath); + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); + } else { + file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); + } + + } + + } else if (($patternStoreData["category"] == "patternType") && PatternData::hasPatternSubtype($patternStoreData["nameDash"])) { + + // grab the partials into a data object for the style guide + $ppExporter = new PatternPartialsExporter(); + $partials = $ppExporter->run($patternStoreData["name"]); + + if (!empty($partials["partials"])) { + + $partials["patternPartial"] = "viewall-".$patternStoreData["nameDash"]."-all"; + $viewAllPage = $viewAllHead.Helper::$filesystemLoader->render("viewall",$partials).$viewAllFoot; // if the pattern directory doesn't exist create it diff --git a/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php b/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php index 1a8adb44c..365902032 100644 --- a/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php +++ b/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php @@ -136,6 +136,17 @@ public function run() { $reset = false; } + // add an overall view all link to the menus with sub-menus + if (!empty($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"])) { + + $navItems["patternTypes"][$patternTypeKey]["patternItems"][] = array("patternPath" => $patternType."/index.html", + "patternName" => "View All", + "patternType" => $patternType, + "patternSubtype" => "all", + "patternPartial" => "viewall-".$patternTypeDash."-all"); + + } + } return $navItems; diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php index e64f5242e..91193d57d 100644 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php +++ b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php @@ -40,7 +40,7 @@ public function run($type = "", $subtype = "") { if (($patternStoreData["category"] == "pattern") && (!$patternStoreData["hidden"]) && ($patternStoreData["depth"] == 2) && (!in_array($patternStoreData["type"],Config::$options["styleGuideExcludes"]))) { - if ((empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["subtype"] == $subtype))) { + if ((($patternStoreData["type"] == $type) && empty($subtype)) || (empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["subtype"] == $subtype))) { $patternPartialData = array(); $patternPartialData["patternName"] = ucwords($patternStoreData["nameClean"]); diff --git a/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php b/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php index 1e7856344..18d30f9b9 100644 --- a/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php +++ b/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php @@ -53,6 +53,12 @@ public function run($navItems) { } + if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { + + $viewAllPaths[$patternTypeDash]["all"] = $patternType; + + } + } } From 9813f7cdd1db7c5d46b0de6f5cc80ddab1f37fec Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:02:35 -0400 Subject: [PATCH 142/166] spacing fix --- .../PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php index 9496701af..b6aab50bf 100644 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php @@ -70,6 +70,7 @@ public function run() { $code = Render::Pattern($srcPath,$data); $modifierCodeExists = true; + } $patternModifiers[] = array("modifierName" => $name, From e3fdb3b1231bbd9294a6bf55aa50fdbf19b69c55 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 11:22:00 -0400 Subject: [PATCH 143/166] smarter search for MQ options --- core/lib/PatternLab/Builder.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index a22286136..5b0c30df7 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -44,14 +44,27 @@ protected function gatherMQs() { $mqs = array(); - foreach(glob(Config::$options["sourceDir"]."/css/*.css") as $filename) { - $data = file_get_contents($filename); - preg_match_all("/(min|max)-width:([ ]+)?(([0-9]{1,5})(\.[0-9]{1,20}|)(px|em))/",$data,$matches); - foreach ($matches[3] as $match) { - if (!in_array($match,$mqs)) { - $mqs[] = $match; + // iterate over all of the other files in the source directory + $directoryIterator = new \RecursiveDirectoryIterator(Config::$options["sourceDir"]); + $objects = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); + + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); + + foreach ($objects as $name => $object) { + + if ($object->isFile() && ($object->getExtension() == "css")) { + + $data = file_get_contents($object->getPathname()); + preg_match_all("/(min|max)-width:([ ]+)?(([0-9]{1,5})(\.[0-9]{1,20}|)(px|em))/",$data,$matches); + foreach ($matches[3] as $match) { + if (!in_array($match,$mqs)) { + $mqs[] = $match; + } } - } + + } + } usort($mqs, "strnatcmp"); From b4e12dd7ecbe55078d0c1b73d69ae9604324f76c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 14:27:59 -0400 Subject: [PATCH 144/166] data json and yaml files can now be "chunked" --- core/lib/PatternLab/Data.php | 81 +++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/core/lib/PatternLab/Data.php b/core/lib/PatternLab/Data.php index 0676bb3f4..3c7c24070 100644 --- a/core/lib/PatternLab/Data.php +++ b/core/lib/PatternLab/Data.php @@ -30,7 +30,7 @@ public static function copy() { } /** - * Gather data from source/_data/_data.json, source/_data/_listitems.json, and pattern-specific json files + * Gather data from any JSON and YAML files in source/_data * * Reserved attributes: * - Data::$store["listItems"] : listItems from listitems.json, duplicated into separate arrays for Data::$store["listItems"]["one"], Data::$store["listItems"]["two"]... etc. @@ -49,30 +49,61 @@ public static function gather($options = array()) { $listItemsJSON = array(); $listItemsYAML = array(); - // gather the data from the main source data.json - if (file_exists(Config::$options["sourceDir"]."/_data/_data.json")) { - $file = file_get_contents(Config::$options["sourceDir"]."/_data/_data.json"); - $dataJSON = json_decode($file,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg("_data/_data.json",$jsonErrorMessage,$data); - } - $found = true; - } + // iterate over all of the other files in the source directory + $directoryIterator = new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_data/"); + $objects = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); - // gather the data from the main source data.yaml - if (file_exists(Config::$options["sourceDir"]."/_data/_data.yaml")) { - $file = file_get_contents(Config::$options["sourceDir"]."/_data/_data.yaml"); - $dataYAML = Yaml::parse($file); - $found = true; - } + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - if (!$found) { - print "Missing a required file, source/_data/_data.json. Aborting.\n"; - exit; + foreach ($objects as $name => $object) { + + $ext = $object->getExtension(); + $data = array(); + $fileName = $object->getFilename(); + $hidden = ($fileName[0] == "_"); + $isFile = $object->isFile(); + $isListItems = strpos("listitems",$fileName); + $pathName = $object->getPathname(); + $pathNameClean = str_replace(Config::$options["sourceDir"]."/","",$pathName); + + if ($isFile && !$hidden && (($ext == "json") || ($ext == "yaml"))) { + + if ($isListItems === false) { + + if ($ext == "json") { + + $file = file_get_contents($pathName); + $data = json_decode($file,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg($pathNameClean,$jsonErrorMessage,$data); + } + + } else if ($ext == "yaml") { + + $file = file_get_contents($pathName); + $data = Yaml::parse($file); + + } + + self::$store = array_replace_recursive(self::$store,$data); + + } else if ($isListItems !== false) { + + $data = ($ext == "json") ? self::getListItems("data/listitems.json") : self::getListItems("data/listitems.yaml","yaml"); + + if (!isset(self::$store["listItems"])) { + self::$store["listItems"] = array(); + } + + self::$store["listItems"] = array_replace_recursive(self::$store["listItems"],$data); + + } + + } + } - self::$store = array_replace_recursive($dataJSON,$dataYAML); - if (is_array(self::$store)) { foreach (self::$reservedKeys as $reservedKey) { if (array_key_exists($reservedKey,self::$store)) { @@ -81,14 +112,14 @@ public static function gather($options = array()) { } } - $listItemsJSON = self::getListItems("data/_listitems.json"); - $listItemsYAML = self::getListItems("data/_listitems.yaml","yaml"); - - self::$store["listItems"] = array_replace_recursive($listItemsJSON,$listItemsYAML); self::$store["cacheBuster"] = Config::$options["cacheBuster"]; self::$store["link"] = array(); self::$store["patternSpecific"] = array(); + unset(self::$store["listItems"]); + print_r(self::$store); + exit; + } /** From fb99498d3312c8b3fe3dce91a2ad259208aefe3c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:58:17 -0400 Subject: [PATCH 145/166] moving files to support the new annotations & data layout --- core/source/{_data => _annotations}/annotations.js | 0 core/source/_data/{_data.json => data.json} | 0 core/source/_data/{_listitems.json => listitems.json} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename core/source/{_data => _annotations}/annotations.js (100%) rename core/source/_data/{_data.json => data.json} (100%) rename core/source/_data/{_listitems.json => listitems.json} (100%) diff --git a/core/source/_data/annotations.js b/core/source/_annotations/annotations.js similarity index 100% rename from core/source/_data/annotations.js rename to core/source/_annotations/annotations.js diff --git a/core/source/_data/_data.json b/core/source/_data/data.json similarity index 100% rename from core/source/_data/_data.json rename to core/source/_data/data.json diff --git a/core/source/_data/_listitems.json b/core/source/_data/listitems.json similarity index 100% rename from core/source/_data/_listitems.json rename to core/source/_data/listitems.json From 40509206a69113a8a18d2346f55ca430c94f2414 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:58:39 -0400 Subject: [PATCH 146/166] generateAnnotations support so markdown can be used --- core/lib/PatternLab/Builder.php | 65 +++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php index 5b0c30df7..69baf064d 100644 --- a/core/lib/PatternLab/Builder.php +++ b/core/lib/PatternLab/Builder.php @@ -14,6 +14,7 @@ use \PatternLab\Config; use \PatternLab\Data; +use \PatternLab\Parsers\Documentation; use \PatternLab\PatternData\Exporters\NavItemsExporter; use \PatternLab\PatternData\Exporters\PatternPartialsExporter; use \PatternLab\PatternData\Exporters\PatternPathDestsExporter; @@ -73,6 +74,70 @@ protected function gatherMQs() { } + protected function generateAnnotations() { + + $annotations = array(); + $annotations["comments"] = array(); + + // iterate over all of the files in the annotations dir + $directoryIterator = new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_annotations"); + $objects = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); + + // make sure dots are skipped + $objects->setFlags(\FilesystemIterator::SKIP_DOTS); + + foreach ($objects as $name => $object) { + + // if it's an .md file parse and generate the proper info + if ($object->isFile() && ($object->getExtension() == "md")) { + + $data = array(); + $data[0] = array(); + + $text = file_get_contents($object->getPathname()); + list($yaml,$markdown) = Documentation::parse($text); + + if (isset($yaml["el"]) || isset($yaml["selector"])) { + $data[0]["el"] = (isset($yaml["el"])) ? $yaml["el"] : $yaml["selector"]; + } else { + $data[0]["el"] = "#someimpossibleselector"; + } + $data[0]["title"] = isset($yaml["title"]) ? $yaml["title"] : ""; + $data[0]["comment"] = $markdown; + + $annotations["comments"] = array_merge($annotations["comments"],$data); + + } + + } + + // read in the old style annotations.js, modify the data and generate JSON array to merge + if (file_exists(Config::$options["sourceDir"]."/_annotations/annotations.js")) { + $text = file_get_contents(Config::$options["sourceDir"]."/_annotations/annotations.js"); + $text = str_replace("var comments = ","",$text); + $text = rtrim($text,";"); + $data = json_decode($text,true); + if ($jsonErrorMessage = JSON::hasError()) { + JSON::lastErrorMsg("_annotations/annotations.js",$jsonErrorMessage,$data); + } + } + + // merge in any data from the old file + $annotations["comments"] = array_merge($annotations["comments"],$data["comments"]); + + // encode the content so it can be written out + $json = json_encode($annotations); + + // make sure annotations/ exists + if (!is_dir(Config::$options["publicDir"]."/annotations")) { + mkdir(Config::$options["publicDir"]."/annotations"); + } + + // write out the new annotations.js file + file_put_contents(Config::$options["publicDir"]."/annotations/annotations.js","var comments = ".$json.";"); + + } + /** * Generates the index page and style guide */ From c51058f5e72c5ac32a6fea275447f123e3062bfc Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:58:48 -0400 Subject: [PATCH 147/166] taking out a debug statement --- core/lib/PatternLab/Data.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/lib/PatternLab/Data.php b/core/lib/PatternLab/Data.php index 3c7c24070..271ab13d9 100644 --- a/core/lib/PatternLab/Data.php +++ b/core/lib/PatternLab/Data.php @@ -116,10 +116,6 @@ public static function gather($options = array()) { self::$store["link"] = array(); self::$store["patternSpecific"] = array(); - unset(self::$store["listItems"]); - print_r(self::$store); - exit; - } /** From f557188c8e1a2b5bd9b4352fbac77e1a98740acf Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:59:15 -0400 Subject: [PATCH 148/166] removing need to move data files --- core/lib/PatternLab/Generator.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index f058e2b4d..851d08ca1 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -81,25 +81,6 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster // render out the patterns and move them to public/patterns $this->generatePatterns(); - // make sure data exists - if (!is_dir(__DIR__.Config::$options["patternPublicDir"]."/data")) { - mkdir(__DIR__.Config::$options["patternPublicDir"]."/data"); - } - - // iterate over the data files and regenerate the entire site if they've changed - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - $fileName = str_replace(Config::$options["sourceDir"]."/_data".DIRECTORY_SEPARATOR,"",$name); - if (($fileName[0] != "_") && $object->isFile()) { - FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); - } - - } // move all of the files unless pattern only is set if ($moveStatic) { From e41b59592865c0925b37812e621fcaab3720f6d9 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:59:25 -0400 Subject: [PATCH 149/166] generate annotations on generate --- core/lib/PatternLab/Generator.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/lib/PatternLab/Generator.php b/core/lib/PatternLab/Generator.php index 851d08ca1..6f7031de5 100644 --- a/core/lib/PatternLab/Generator.php +++ b/core/lib/PatternLab/Generator.php @@ -81,6 +81,8 @@ public function generate($enableCSS = false, $moveStatic = true, $noCacheBuster // render out the patterns and move them to public/patterns $this->generatePatterns(); + // render the annotations as a js file + $this->generateAnnotations(); // move all of the files unless pattern only is set if ($moveStatic) { From 06ad322cc64d08170e5b06daf55d6afa25874b2e Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 15:59:49 -0400 Subject: [PATCH 150/166] watch _annotations for changes --- core/lib/PatternLab/Watcher.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index 65cae3c69..2f7c8c4c3 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -142,27 +142,22 @@ public function watch($reload = false, $moveStatic = true, $noCacheBuster = fals } // iterate over the data files and regenerate the entire site if they've changed - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_data/"), \RecursiveIteratorIterator::SELF_FIRST); + $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_annotations/"), \RecursiveIteratorIterator::SELF_FIRST); // make sure dots are skipped $objects->setFlags(\FilesystemIterator::SKIP_DOTS); foreach($objects as $name => $object) { - $fileName = str_replace(Config::$options["sourceDir"]."/_data".DIRECTORY_SEPARATOR,"",$name); + $fileName = str_replace(Config::$options["sourceDir"]."/_annotations".DIRECTORY_SEPARATOR,"",$name); $mt = $object->getMTime(); if (!isset($o->$fileName)) { $o->$fileName = $mt; - if (($fileName[0] != "_") && $object->isFile()) { - FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); - } + $this->updateSite($fileName,"added"); } else if ($o->$fileName != $mt) { $o->$fileName = $mt; $this->updateSite($fileName,"changed"); - if (($fileName[0] != "_") && $object->isFile()) { - FileUtil::moveStaticFile("_data/".$fileName,"","_data","data"); - } } } From e1b3f701d13207c3ec46a13194917f444ccac061 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 16:00:00 -0400 Subject: [PATCH 151/166] generate annotations when the site is regenerated --- core/lib/PatternLab/Watcher.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php index 2f7c8c4c3..21357e48c 100644 --- a/core/lib/PatternLab/Watcher.php +++ b/core/lib/PatternLab/Watcher.php @@ -254,6 +254,7 @@ private function updateSite($fileName,$message,$verbose = true) { $this->generateStyleguide(); $this->generateViewAllPages(); $this->generatePatterns(); + $this->generateAnnotations(); Util::updateChangeTime(); From 28e7ebc78c066891cce1c1a08dc674a3e37f8bc8 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 16:00:14 -0400 Subject: [PATCH 152/166] changing the public loc of annotations --- core/templates/pattern-header-footer/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/templates/pattern-header-footer/footer.html b/core/templates/pattern-header-footer/footer.html index d36ac9880..64db6455a 100644 --- a/core/templates/pattern-header-footer/footer.html +++ b/core/templates/pattern-header-footer/footer.html @@ -46,7 +46,7 @@ (function() { if (self != top) { var cb = '{{ cacheBuster}}'; - var js = [ { "src": "styleguide/js/vendor/jwerty.js", "dep": [ "styleguide/js/postmessage.js", { "src": "data/annotations.js", "dep": [ "styleguide/js/annotations-pattern.js" ] }, "styleguide/js/code-pattern.js" ] } ]; + var js = [ { "src": "styleguide/js/vendor/jwerty.js", "dep": [ "styleguide/js/postmessage.js", { "src": "annotations/annotations.js", "dep": [ "styleguide/js/annotations-pattern.js" ] }, "styleguide/js/code-pattern.js" ] } ]; scriptLoader.run(js,cb,'pl-js-insert'); } })(); From a1c0057f9a6a1dd7d19502388af5c2a77dfd2a3b Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 16:08:29 -0400 Subject: [PATCH 153/166] another fix from @fstop --- core/styleguide/js/postmessage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/styleguide/js/postmessage.js b/core/styleguide/js/postmessage.js index 30f027046..abb1b5d48 100644 --- a/core/styleguide/js/postmessage.js +++ b/core/styleguide/js/postmessage.js @@ -34,7 +34,7 @@ if (self != top) { var target = this.getAttribute("target"); if ((target != undefined) && ((target == "_parent") || (target == "_blank"))) { // just do normal stuff - } else if (href.length && href !== "#") { + } else if (href && href !== "#") { e.preventDefault(); window.location.replace(href); } else { From 713098396975a372c071e3b5af7632cdf82bf210 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 3 Jun 2014 16:16:01 -0400 Subject: [PATCH 154/166] move the shared pattern header and footer to a _meta dir --- core/lib/PatternLab/Template/Helper.php | 4 ++-- .../{_patterns/00-atoms/00-meta => _meta}/_00-head.mustache | 0 .../{_patterns/00-atoms/00-meta => _meta}/_01-foot.mustache | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename core/source/{_patterns/00-atoms/00-meta => _meta}/_00-head.mustache (100%) rename core/source/{_patterns/00-atoms/00-meta => _meta}/_01-foot.mustache (100%) diff --git a/core/lib/PatternLab/Template/Helper.php b/core/lib/PatternLab/Template/Helper.php index 63f7c2712..2062e4e1b 100644 --- a/core/lib/PatternLab/Template/Helper.php +++ b/core/lib/PatternLab/Template/Helper.php @@ -35,8 +35,8 @@ public static function setup() { $extraFoot = file_get_contents(__DIR__."/../../../templates/pattern-header-footer/footer-pattern.html"); // gather the user-defined header and footer information - $patternHeadPath = __DIR__."/..".Config::$options["patternSourceDir"]."00-atoms/00-meta/_00-head.mustache"; - $patternFootPath = __DIR__."/..".Config::$options["patternSourceDir"]."00-atoms/00-meta/_01-foot.mustache"; + $patternHeadPath = Config::$options["sourceDir"]."/_meta/_00-head.mustache"; + $patternFootPath = Config::$options["sourceDir"]."/_meta/_01-foot.mustache"; $patternHead = (file_exists($patternHeadPath)) ? file_get_contents($patternHeadPath) : ""; $patternFoot = (file_exists($patternFootPath)) ? file_get_contents($patternFootPath) : ""; diff --git a/core/source/_patterns/00-atoms/00-meta/_00-head.mustache b/core/source/_meta/_00-head.mustache similarity index 100% rename from core/source/_patterns/00-atoms/00-meta/_00-head.mustache rename to core/source/_meta/_00-head.mustache diff --git a/core/source/_patterns/00-atoms/00-meta/_01-foot.mustache b/core/source/_meta/_01-foot.mustache similarity index 100% rename from core/source/_patterns/00-atoms/00-meta/_01-foot.mustache rename to core/source/_meta/_01-foot.mustache From 34d3ab2c0e1ee34b0cd3de52776572da21a88112 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 11 Jul 2016 22:52:49 -0400 Subject: [PATCH 155/166] cleaning the old work out --- composer.json | 16 - composer.lock | 644 -- core/autoReloadServer.php | 45 - core/builder.php | 135 - core/config/config.ini.default | 52 - core/lib/CSSRuleSaver/CSSRuleSaver.php | 266 - core/lib/CSSRuleSaver/LICENSE | 20 - core/lib/CSSRuleSaver/LICENSE.SelectorDOM | 22 - core/lib/CSSRuleSaver/SelectorDOM.php | 178 - core/lib/Mustache/Autoloader.php | 69 - core/lib/Mustache/Compiler.php | 475 - core/lib/Mustache/Context.php | 149 - core/lib/Mustache/Engine.php | 729 -- core/lib/Mustache/Exception.php | 18 - .../Exception/InvalidArgumentException.php | 18 - .../lib/Mustache/Exception/LogicException.php | 18 - .../Mustache/Exception/RuntimeException.php | 18 - .../Mustache/Exception/SyntaxException.php | 29 - .../Exception/UnknownFilterException.php | 29 - .../Exception/UnknownHelperException.php | 29 - .../Exception/UnknownTemplateException.php | 29 - core/lib/Mustache/HelperCollection.php | 170 - core/lib/Mustache/LICENSE | 20 - core/lib/Mustache/LambdaHelper.php | 49 - core/lib/Mustache/Loader.php | 28 - core/lib/Mustache/Loader/ArrayLoader.php | 78 - core/lib/Mustache/Loader/CascadingLoader.php | 69 - core/lib/Mustache/Loader/FilesystemLoader.php | 120 - core/lib/Mustache/Loader/InlineLoader.php | 121 - core/lib/Mustache/Loader/MutableLoader.php | 32 - core/lib/Mustache/Loader/StringLoader.php | 40 - core/lib/Mustache/Logger.php | 135 - core/lib/Mustache/Logger/AbstractLogger.php | 121 - core/lib/Mustache/Logger/StreamLogger.php | 193 - core/lib/Mustache/Parser.php | 91 - core/lib/Mustache/Template.php | 177 - core/lib/Mustache/Tokenizer.php | 315 - core/lib/PatternLab/Builder.php | 329 - core/lib/PatternLab/Config.php | 159 - core/lib/PatternLab/Console.php | 351 - core/lib/PatternLab/Data.php | 231 - core/lib/PatternLab/FileUtil.php | 62 - core/lib/PatternLab/Generator.php | 168 - core/lib/PatternLab/JSON.php | 53 - core/lib/PatternLab/Migrator.php | 141 - core/lib/PatternLab/Parsers/Documentation.php | 125 - core/lib/PatternLab/Parsers/Plugins/KSS.php | 30 - core/lib/PatternLab/PatternData.php | 133 - core/lib/PatternLab/PatternData/Exporter.php | 21 - .../Exporters/DataLinkExporter.php | 40 - .../Exporters/DataMergeExporter.php | 54 - .../Exporters/NavItemsExporter.php | 156 - .../Exporters/PatternPartialsExporter.php | 79 - .../Exporters/PatternPathDestsExporter.php | 51 - .../Exporters/PatternPathSrcExporter.php | 52 - .../Exporters/ViewAllPathsExporter.php | 72 - core/lib/PatternLab/PatternData/Helper.php | 21 - .../PatternData/Helpers/LineageHelper.php | 165 - .../PatternData/Helpers/PatternCodeHelper.php | 72 - .../Helpers/PatternStateHelper.php | 123 - .../Plugins/CSSRuleSaverHelperPlugin.php | 8 - .../Helpers/Plugins/KSSHelperPlugin.php | 99 - core/lib/PatternLab/PatternData/Rule.php | 108 - .../PatternData/Rules/DocumentationRule.php | 94 - .../Rules/PatternInfoListItemsRule.php | 68 - .../PatternData/Rules/PatternInfoRule.php | 77 - .../PatternData/Rules/PatternRule.php | 100 - .../PatternData/Rules/PatternSubtypeRule.php | 77 - .../PatternData/Rules/PatternTypeRule.php | 68 - .../PatternData/Rules/PseudoPatternRule.php | 143 - core/lib/PatternLab/PatternEngine.php | 55 - core/lib/PatternLab/PatternEngine/Loader.php | 317 - .../PatternEngine/Loaders/MustacheLoader.php | 158 - .../PatternEngine/Loaders/TwigLoader.php | 232 - core/lib/PatternLab/PatternEngine/Rule.php | 33 - .../PatternEngine/Rules/MustacheRule.php | 38 - .../PatternEngine/Rules/TwigRule.php | 37 - core/lib/PatternLab/Render.php | 63 - core/lib/PatternLab/Snapshot.php | 143 - .../PatternLab/Snapshot/FilterIterator.php | 26 - core/lib/PatternLab/StarterKit.php | 147 - core/lib/PatternLab/Template/Helper.php | 56 - core/lib/PatternLab/Template/Loader.php | 38 - core/lib/PatternLab/Util.php | 147 - core/lib/PatternLab/Watcher.php | 274 - core/lib/Seld/JsonLint/JsonParser.php | 464 - core/lib/Seld/JsonLint/LICENSE | 19 - core/lib/Seld/JsonLint/Lexer.php | 229 - core/lib/Seld/JsonLint/ParsingException.php | 28 - core/lib/Seld/JsonLint/Undefined.php | 16 - core/lib/Wrench/Application/Application.php | 34 - .../Application/AutoReloadApplication.php | 85 - .../Application/PageFollowApplication.php | 71 - core/lib/Wrench/BasicServer.php | 70 - core/lib/Wrench/Client.php | 265 - core/lib/Wrench/Connection.php | 538 - core/lib/Wrench/ConnectionManager.php | 332 - .../Wrench/Exception/BadRequestException.php | 22 - core/lib/Wrench/Exception/CloseException.php | 25 - .../Wrench/Exception/ConnectionException.php | 7 - core/lib/Wrench/Exception/Exception.php | 7 - core/lib/Wrench/Exception/FrameException.php | 8 - .../Wrench/Exception/HandshakeException.php | 22 - .../Exception/InvalidOriginException.php | 25 - .../lib/Wrench/Exception/PayloadException.php | 8 - .../Wrench/Exception/RateLimiterException.php | 20 - core/lib/Wrench/Exception/SocketException.php | 8 - core/lib/Wrench/Frame/Frame.php | 190 - core/lib/Wrench/Frame/HybiFrame.php | 376 - .../Listener/HandshakeRequestListener.php | 19 - core/lib/Wrench/Listener/Listener.php | 10 - core/lib/Wrench/Listener/OriginPolicy.php | 76 - core/lib/Wrench/Listener/RateLimiter.php | 230 - core/lib/Wrench/Payload/HybiPayload.php | 22 - core/lib/Wrench/Payload/Payload.php | 217 - core/lib/Wrench/Payload/PayloadHandler.php | 110 - core/lib/Wrench/Protocol/Hybi10Protocol.php | 35 - core/lib/Wrench/Protocol/HybiProtocol.php | 24 - core/lib/Wrench/Protocol/Protocol.php | 816 -- core/lib/Wrench/Protocol/Rfc6455Protocol.php | 36 - core/lib/Wrench/Resource.php | 12 - core/lib/Wrench/Server.php | 303 - core/lib/Wrench/Socket/ClientSocket.php | 105 - core/lib/Wrench/Socket/ServerClientSocket.php | 25 - core/lib/Wrench/Socket/ServerSocket.php | 126 - core/lib/Wrench/Socket/Socket.php | 322 - core/lib/Wrench/Socket/UriSocket.php | 118 - .../Tests/Application/EchoApplicationTest.php | 57 - core/lib/Wrench/Tests/BasicServerTest.php | 119 - core/lib/Wrench/Tests/ClientTest.php | 173 - .../Wrench/Tests/ConnectionManagerTest.php | 101 - core/lib/Wrench/Tests/ConnectionTest.php | 386 - .../Tests/Frame/BaseSubclassFrameTest.php | 29 - core/lib/Wrench/Tests/Frame/FrameTest.php | 168 - core/lib/Wrench/Tests/Frame/HybiFrameTest.php | 14 - .../Wrench/Tests/Listener/ListenerTest.php | 23 - .../Tests/Listener/OriginPolicyTest.php | 110 - .../Wrench/Tests/Listener/RateLimiterTest.php | 67 - .../Wrench/Tests/Payload/HybiPayloadTest.php | 14 - core/lib/Wrench/Tests/Payload/PayloadTest.php | 140 - .../Wrench/Tests/Protocol/ProtocolTest.php | 180 - .../Tests/Protocol/Rfc6455ProtocolTest.php | 14 - core/lib/Wrench/Tests/ServerTest.php | 78 - core/lib/Wrench/Tests/ServerTestHelper.php | 144 - .../Wrench/Tests/Socket/ClientSocketTest.php | 167 - .../Tests/Socket/ServerClientSocketTest.php | 42 - .../Wrench/Tests/Socket/ServerSocketTest.php | 13 - core/lib/Wrench/Tests/Socket/SocketTest.php | 47 - .../lib/Wrench/Tests/Socket/UriSocketTest.php | 56 - core/lib/Wrench/Tests/Test.php | 61 - core/lib/Wrench/Tests/bootstrap.php | 11 - core/lib/Wrench/Tests/server.php | 16 - core/lib/Wrench/Util/Configurable.php | 67 - core/lib/Wrench/Util/Ssl.php | 51 - core/lib/alchemy/zippy/.gitignore | 6 - core/lib/alchemy/zippy/.travis.yml | 24 - core/lib/alchemy/zippy/CHANGELOG.md | 22 - core/lib/alchemy/zippy/LICENSE | 21 - core/lib/alchemy/zippy/README.md | 80 - core/lib/alchemy/zippy/composer.json | 41 - core/lib/alchemy/zippy/composer.lock | 1092 --- .../alchemy/zippy/phpunit-functional.xml.dist | 31 - core/lib/alchemy/zippy/phpunit.xml.dist | 31 - .../Alchemy/Zippy/Adapter/AbstractAdapter.php | 246 - .../Zippy/Adapter/AbstractBinaryAdapter.php | 231 - .../Zippy/Adapter/AbstractTarAdapter.php | 424 - .../Zippy/Adapter/AdapterContainer.php | 148 - .../Zippy/Adapter/AdapterInterface.php | 131 - .../Zippy/Adapter/BSDTar/TarBSDTarAdapter.php | 79 - .../Adapter/BSDTar/TarBz2BSDTarAdapter.php | 25 - .../Adapter/BSDTar/TarGzBSDTarAdapter.php | 25 - .../Zippy/Adapter/BinaryAdapterInterface.php | 94 - .../Adapter/GNUTar/TarBz2GNUTarAdapter.php | 25 - .../Zippy/Adapter/GNUTar/TarGNUTarAdapter.php | 79 - .../Adapter/GNUTar/TarGzGNUTarAdapter.php | 25 - .../Zippy/Adapter/Resource/FileResource.php | 31 - .../Adapter/Resource/ResourceInterface.php | 23 - .../Adapter/Resource/ZipArchiveResource.php | 31 - .../VersionProbe/AbstractTarVersionProbe.php | 75 - .../VersionProbe/BSDTarVersionProbe.php | 24 - .../VersionProbe/GNUTarVersionProbe.php | 24 - .../VersionProbe/VersionProbeInterface.php | 26 - .../VersionProbe/ZipExtensionVersionProbe.php | 24 - .../Adapter/VersionProbe/ZipVersionProbe.php | 96 - .../src/Alchemy/Zippy/Adapter/ZipAdapter.php | 361 - .../Zippy/Adapter/ZipExtensionAdapter.php | 339 - .../src/Alchemy/Zippy/Archive/Archive.php | 136 - .../Zippy/Archive/ArchiveInterface.php | 74 - .../src/Alchemy/Zippy/Archive/Member.php | 133 - .../Alchemy/Zippy/Archive/MemberInterface.php | 66 - .../Zippy/Exception/ExceptionInterface.php | 16 - .../Exception/FormatNotSupportedException.php | 16 - .../Alchemy/Zippy/Exception/IOException.php | 16 - .../Exception/InvalidArgumentException.php | 16 - .../NoAdapterOnPlatformException.php | 16 - .../Zippy/Exception/NotSupportedException.php | 16 - .../Zippy/Exception/RuntimeException.php | 16 - .../Exception/TargetLocatorException.php | 28 - .../FileStrategy/FileStrategyInterface.php | 29 - .../Zippy/FileStrategy/TB2FileStrategy.php | 43 - .../Zippy/FileStrategy/TBz2FileStrategy.php | 43 - .../Zippy/FileStrategy/TGzFileStrategy.php | 43 - .../Zippy/FileStrategy/TarBz2FileStrategy.php | 43 - .../Zippy/FileStrategy/TarFileStrategy.php | 43 - .../Zippy/FileStrategy/TarGzFileStrategy.php | 43 - .../Zippy/FileStrategy/ZipFileStrategy.php | 43 - .../Zippy/Parser/BSDTarOutputParser.php | 115 - .../Zippy/Parser/GNUTarOutputParser.php | 98 - .../Alchemy/Zippy/Parser/ParserFactory.php | 45 - .../Alchemy/Zippy/Parser/ParserInterface.php | 46 - .../Alchemy/Zippy/Parser/ZipOutputParser.php | 96 - .../ProcessBuilder/ProcessBuilderFactory.php | 71 - .../ProcessBuilderFactoryInterface.php | 45 - .../Alchemy/Zippy/Resource/RequestMapper.php | 63 - .../src/Alchemy/Zippy/Resource/Resource.php | 106 - .../Zippy/Resource/ResourceCollection.php | 86 - .../Zippy/Resource/ResourceManager.php | 105 - .../Zippy/Resource/ResourceTeleporter.php | 55 - .../Alchemy/Zippy/Resource/TargetLocator.php | 156 - .../Teleporter/AbstractTeleporter.php | 53 - .../Resource/Teleporter/GuzzleTeleporter.php | 74 - .../Resource/Teleporter/LocalTeleporter.php | 71 - .../Resource/Teleporter/StreamTeleporter.php | 61 - .../Teleporter/TeleporterInterface.php | 28 - .../Zippy/Resource/TeleporterContainer.php | 79 - .../alchemy/zippy/src/Alchemy/Zippy/Zippy.php | 213 - core/lib/autoload.php | 7 - core/lib/composer/ClassLoader.php | 364 - core/lib/composer/autoload_classmap.php | 9 - core/lib/composer/autoload_namespaces.php | 22 - core/lib/composer/autoload_psr4.php | 9 - core/lib/composer/autoload_real.php | 53 - core/lib/composer/installed.json | 643 -- core/lib/doctrine/collections/.gitignore | 1 - core/lib/doctrine/collections/.travis.yml | 10 - core/lib/doctrine/collections/LICENSE | 19 - core/lib/doctrine/collections/README.md | 17 - core/lib/doctrine/collections/composer.json | 26 - .../Collections/AbstractLazyCollection.php | 343 - .../Common/Collections/ArrayCollection.php | 385 - .../Common/Collections/Collection.php | 260 - .../Doctrine/Common/Collections/Criteria.php | 245 - .../Expr/ClosureExpressionVisitor.php | 227 - .../Common/Collections/Expr/Comparison.php | 103 - .../Collections/Expr/CompositeExpression.php | 90 - .../Common/Collections/Expr/Expression.php | 35 - .../Collections/Expr/ExpressionVisitor.php | 82 - .../Common/Collections/Expr/Value.php | 52 - .../Common/Collections/ExpressionBuilder.php | 166 - .../Common/Collections/Selectable.php | 48 - .../lib/doctrine/collections/phpunit.xml.dist | 31 - core/lib/guzzle/guzzle/.gitignore | 27 - core/lib/guzzle/guzzle/.travis.yml | 30 - core/lib/guzzle/guzzle/CHANGELOG.md | 788 -- core/lib/guzzle/guzzle/LICENSE | 19 - core/lib/guzzle/guzzle/README.md | 36 - core/lib/guzzle/guzzle/UPGRADING.md | 537 - core/lib/guzzle/guzzle/build.xml | 45 - core/lib/guzzle/guzzle/composer.json | 74 - core/lib/guzzle/guzzle/phar-stub.php | 28 - .../guzzle/guzzle/phing/build.properties.dist | 16 - .../guzzle/phing/imports/dependencies.xml | 33 - .../guzzle/guzzle/phing/imports/deploy.xml | 142 - .../guzzle/phing/tasks/ComposerLintTask.php | 152 - .../phing/tasks/GuzzlePearPharPackageTask.php | 338 - .../guzzle/phing/tasks/GuzzleSubSplitTask.php | 385 - core/lib/guzzle/guzzle/phpunit.xml.dist | 48 - .../Guzzle/Batch/AbstractBatchDecorator.php | 66 - .../guzzle/guzzle/src/Guzzle/Batch/Batch.php | 92 - .../guzzle/src/Guzzle/Batch/BatchBuilder.php | 199 - .../src/Guzzle/Batch/BatchClosureDivisor.php | 39 - .../src/Guzzle/Batch/BatchClosureTransfer.php | 40 - .../src/Guzzle/Batch/BatchCommandTransfer.php | 75 - .../Guzzle/Batch/BatchDivisorInterface.php | 18 - .../src/Guzzle/Batch/BatchInterface.php | 32 - .../src/Guzzle/Batch/BatchRequestTransfer.php | 65 - .../src/Guzzle/Batch/BatchSizeDivisor.php | 47 - .../Guzzle/Batch/BatchTransferInterface.php | 16 - .../Exception/BatchTransferException.php | 90 - .../Guzzle/Batch/ExceptionBufferingBatch.php | 50 - .../guzzle/src/Guzzle/Batch/FlushingBatch.php | 60 - .../guzzle/src/Guzzle/Batch/HistoryBatch.php | 39 - .../src/Guzzle/Batch/NotifyingBatch.php | 38 - .../guzzle/src/Guzzle/Batch/composer.json | 31 - .../src/Guzzle/Cache/AbstractCacheAdapter.php | 21 - .../src/Guzzle/Cache/CacheAdapterFactory.php | 117 - .../Guzzle/Cache/CacheAdapterInterface.php | 55 - .../src/Guzzle/Cache/ClosureCacheAdapter.php | 57 - .../src/Guzzle/Cache/DoctrineCacheAdapter.php | 41 - .../src/Guzzle/Cache/NullCacheAdapter.php | 31 - .../src/Guzzle/Cache/Zf1CacheAdapter.php | 44 - .../src/Guzzle/Cache/Zf2CacheAdapter.php | 41 - .../guzzle/src/Guzzle/Cache/composer.json | 27 - .../Guzzle/Common/AbstractHasDispatcher.php | 49 - .../guzzle/src/Guzzle/Common/Collection.php | 403 - .../guzzle/guzzle/src/Guzzle/Common/Event.php | 52 - .../Exception/BadMethodCallException.php | 5 - .../Common/Exception/ExceptionCollection.php | 108 - .../Common/Exception/GuzzleException.php | 8 - .../Exception/InvalidArgumentException.php | 5 - .../Common/Exception/RuntimeException.php | 5 - .../Exception/UnexpectedValueException.php | 5 - .../src/Guzzle/Common/FromConfigInterface.php | 18 - .../Guzzle/Common/HasDispatcherInterface.php | 54 - .../src/Guzzle/Common/ToArrayInterface.php | 16 - .../guzzle/src/Guzzle/Common/Version.php | 29 - .../guzzle/src/Guzzle/Common/composer.json | 20 - .../Http/AbstractEntityBodyDecorator.php | 221 - .../src/Guzzle/Http/CachingEntityBody.php | 229 - .../guzzle/guzzle/src/Guzzle/Http/Client.php | 490 - .../src/Guzzle/Http/ClientInterface.php | 223 - .../src/Guzzle/Http/Curl/CurlHandle.php | 464 - .../guzzle/src/Guzzle/Http/Curl/CurlMulti.php | 367 - .../Guzzle/Http/Curl/CurlMultiInterface.php | 58 - .../src/Guzzle/Http/Curl/CurlMultiProxy.php | 150 - .../src/Guzzle/Http/Curl/CurlVersion.php | 66 - .../src/Guzzle/Http/Curl/RequestMediator.php | 147 - .../guzzle/src/Guzzle/Http/EntityBody.php | 201 - .../src/Guzzle/Http/EntityBodyInterface.php | 73 - .../Http/Exception/BadResponseException.php | 69 - .../ClientErrorResponseException.php | 8 - .../CouldNotRewindStreamException.php | 7 - .../Guzzle/Http/Exception/CurlException.php | 101 - .../Guzzle/Http/Exception/HttpException.php | 10 - .../Http/Exception/MultiTransferException.php | 145 - .../Http/Exception/RequestException.php | 39 - .../ServerErrorResponseException.php | 8 - .../Exception/TooManyRedirectsException.php | 5 - .../src/Guzzle/Http/IoEmittingEntityBody.php | 83 - .../Guzzle/Http/Message/AbstractMessage.php | 220 - .../Http/Message/EntityEnclosingRequest.php | 247 - .../EntityEnclosingRequestInterface.php | 137 - .../guzzle/src/Guzzle/Http/Message/Header.php | 182 - .../Http/Message/Header/CacheControl.php | 121 - .../Http/Message/Header/HeaderCollection.php | 108 - .../Http/Message/Header/HeaderFactory.php | 26 - .../Message/Header/HeaderFactoryInterface.php | 19 - .../Http/Message/Header/HeaderInterface.php | 83 - .../src/Guzzle/Http/Message/Header/Link.php | 93 - .../Guzzle/Http/Message/MessageInterface.php | 102 - .../src/Guzzle/Http/Message/PostFile.php | 124 - .../Guzzle/Http/Message/PostFileInterface.php | 83 - .../src/Guzzle/Http/Message/Request.php | 638 -- .../Guzzle/Http/Message/RequestFactory.php | 359 - .../Http/Message/RequestFactoryInterface.php | 105 - .../Guzzle/Http/Message/RequestInterface.php | 318 - .../src/Guzzle/Http/Message/Response.php | 968 -- .../guzzle/src/Guzzle/Http/Mimetypes.php | 962 -- .../Http/QueryAggregator/CommaAggregator.php | 20 - .../QueryAggregator/DuplicateAggregator.php | 22 - .../Http/QueryAggregator/PhpAggregator.php | 27 - .../QueryAggregatorInterface.php | 22 - .../guzzle/src/Guzzle/Http/QueryString.php | 297 - .../src/Guzzle/Http/ReadLimitEntityBody.php | 106 - .../guzzle/src/Guzzle/Http/RedirectPlugin.php | 250 - .../src/Guzzle/Http/Resources/cacert.pem | 3785 ------- .../guzzle/src/Guzzle/Http/StaticClient.php | 157 - .../lib/guzzle/guzzle/src/Guzzle/Http/Url.php | 554 -- .../guzzle/src/Guzzle/Http/composer.json | 32 - .../src/Guzzle/Inflection/Inflector.php | 38 - .../Guzzle/Inflection/InflectorInterface.php | 27 - .../Guzzle/Inflection/MemoizingInflector.php | 70 - .../Inflection/PreComputedInflector.php | 59 - .../src/Guzzle/Inflection/composer.json | 26 - .../src/Guzzle/Iterator/AppendIterator.php | 19 - .../src/Guzzle/Iterator/ChunkedIterator.php | 56 - .../src/Guzzle/Iterator/FilterIterator.php | 36 - .../src/Guzzle/Iterator/MapIterator.php | 34 - .../Guzzle/Iterator/MethodProxyIterator.php | 27 - .../guzzle/src/Guzzle/Iterator/README.md | 25 - .../guzzle/src/Guzzle/Iterator/composer.json | 27 - .../src/Guzzle/Log/AbstractLogAdapter.php | 16 - .../guzzle/src/Guzzle/Log/ArrayLogAdapter.php | 34 - .../src/Guzzle/Log/ClosureLogAdapter.php | 23 - .../src/Guzzle/Log/LogAdapterInterface.php | 18 - .../src/Guzzle/Log/MessageFormatter.php | 179 - .../src/Guzzle/Log/MonologLogAdapter.php | 34 - .../guzzle/src/Guzzle/Log/PsrLogAdapter.php | 36 - .../guzzle/src/Guzzle/Log/Zf1LogAdapter.php | 24 - .../guzzle/src/Guzzle/Log/Zf2LogAdapter.php | 21 - .../guzzle/src/Guzzle/Log/composer.json | 29 - .../src/Guzzle/Parser/Cookie/CookieParser.php | 131 - .../Parser/Cookie/CookieParserInterface.php | 33 - .../Parser/Message/AbstractMessageParser.php | 58 - .../Guzzle/Parser/Message/MessageParser.php | 110 - .../Parser/Message/MessageParserInterface.php | 27 - .../Parser/Message/PeclHttpMessageParser.php | 48 - .../src/Guzzle/Parser/ParserRegistry.php | 75 - .../Parser/UriTemplate/PeclUriTemplate.php | 26 - .../Guzzle/Parser/UriTemplate/UriTemplate.php | 254 - .../UriTemplate/UriTemplateInterface.php | 21 - .../src/Guzzle/Parser/Url/UrlParser.php | 48 - .../Guzzle/Parser/Url/UrlParserInterface.php | 19 - .../guzzle/src/Guzzle/Parser/composer.json | 19 - .../src/Guzzle/Plugin/Async/AsyncPlugin.php | 84 - .../src/Guzzle/Plugin/Async/composer.json | 27 - .../Backoff/AbstractBackoffStrategy.php | 91 - .../AbstractErrorCodeBackoffStrategy.php | 40 - .../Guzzle/Plugin/Backoff/BackoffLogger.php | 76 - .../Guzzle/Plugin/Backoff/BackoffPlugin.php | 126 - .../Backoff/BackoffStrategyInterface.php | 30 - .../Backoff/CallbackBackoffStrategy.php | 47 - .../Backoff/ConstantBackoffStrategy.php | 34 - .../Plugin/Backoff/CurlBackoffStrategy.php | 28 - .../Backoff/ExponentialBackoffStrategy.php | 25 - .../Plugin/Backoff/HttpBackoffStrategy.php | 30 - .../Plugin/Backoff/LinearBackoffStrategy.php | 36 - .../Backoff/ReasonPhraseBackoffStrategy.php | 25 - .../Backoff/TruncatedBackoffStrategy.php | 36 - .../src/Guzzle/Plugin/Backoff/composer.json | 28 - .../Cache/CacheKeyProviderInterface.php | 11 - .../src/Guzzle/Plugin/Cache/CachePlugin.php | 353 - .../Plugin/Cache/CacheStorageInterface.php | 43 - .../Plugin/Cache/CallbackCanCacheStrategy.php | 53 - .../Cache/CanCacheStrategyInterface.php | 30 - .../Plugin/Cache/DefaultCacheKeyProvider.php | 46 - .../Plugin/Cache/DefaultCacheStorage.php | 251 - .../Plugin/Cache/DefaultCanCacheStrategy.php | 32 - .../Plugin/Cache/DefaultRevalidation.php | 174 - .../Guzzle/Plugin/Cache/DenyRevalidation.php | 19 - .../Plugin/Cache/RevalidationInterface.php | 32 - .../Guzzle/Plugin/Cache/SkipRevalidation.php | 19 - .../src/Guzzle/Plugin/Cache/composer.json | 28 - .../src/Guzzle/Plugin/Cookie/Cookie.php | 538 - .../Cookie/CookieJar/ArrayCookieJar.php | 237 - .../Cookie/CookieJar/CookieJarInterface.php | 85 - .../Plugin/Cookie/CookieJar/FileCookieJar.php | 65 - .../src/Guzzle/Plugin/Cookie/CookiePlugin.php | 70 - .../Exception/InvalidCookieException.php | 7 - .../src/Guzzle/Plugin/Cookie/composer.json | 27 - .../Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php | 46 - .../src/Guzzle/Plugin/CurlAuth/composer.json | 27 - .../ErrorResponseExceptionInterface.php | 22 - .../ErrorResponse/ErrorResponsePlugin.php | 72 - .../Exception/ErrorResponseException.php | 7 - .../Guzzle/Plugin/ErrorResponse/composer.json | 27 - .../Guzzle/Plugin/History/HistoryPlugin.php | 163 - .../src/Guzzle/Plugin/History/composer.json | 27 - .../src/Guzzle/Plugin/Log/LogPlugin.php | 161 - .../src/Guzzle/Plugin/Log/composer.json | 28 - .../Plugin/Md5/CommandContentMd5Plugin.php | 57 - .../Guzzle/Plugin/Md5/Md5ValidatorPlugin.php | 88 - .../src/Guzzle/Plugin/Md5/composer.json | 27 - .../src/Guzzle/Plugin/Mock/MockPlugin.php | 245 - .../src/Guzzle/Plugin/Mock/composer.json | 27 - .../src/Guzzle/Plugin/Oauth/OauthPlugin.php | 306 - .../src/Guzzle/Plugin/Oauth/composer.json | 27 - .../guzzle/src/Guzzle/Plugin/composer.json | 44 - .../Guzzle/Service/AbstractConfigLoader.php | 177 - .../Guzzle/Service/Builder/ServiceBuilder.php | 189 - .../Builder/ServiceBuilderInterface.php | 40 - .../Service/Builder/ServiceBuilderLoader.php | 89 - .../Guzzle/Service/CachingConfigLoader.php | 46 - .../guzzle/src/Guzzle/Service/Client.php | 297 - .../src/Guzzle/Service/ClientInterface.php | 68 - .../Service/Command/AbstractCommand.php | 390 - .../Guzzle/Service/Command/ClosureCommand.php | 41 - .../Service/Command/CommandInterface.php | 128 - .../Command/CreateResponseClassEvent.php | 32 - .../Command/DefaultRequestSerializer.php | 169 - .../Service/Command/DefaultResponseParser.php | 55 - .../Service/Command/Factory/AliasFactory.php | 39 - .../Command/Factory/CompositeFactory.php | 154 - .../Command/Factory/ConcreteClassFactory.php | 47 - .../Command/Factory/FactoryInterface.php | 21 - .../Service/Command/Factory/MapFactory.php | 27 - .../Factory/ServiceDescriptionFactory.php | 71 - .../Request/AbstractRequestVisitor.php | 69 - .../LocationVisitor/Request/BodyVisitor.php | 58 - .../LocationVisitor/Request/HeaderVisitor.php | 44 - .../LocationVisitor/Request/JsonVisitor.php | 63 - .../Request/PostFieldVisitor.php | 18 - .../Request/PostFileVisitor.php | 24 - .../LocationVisitor/Request/QueryVisitor.php | 18 - .../Request/RequestVisitorInterface.php | 31 - .../Request/ResponseBodyVisitor.php | 18 - .../LocationVisitor/Request/XmlVisitor.php | 252 - .../Response/AbstractResponseVisitor.php | 26 - .../LocationVisitor/Response/BodyVisitor.php | 23 - .../Response/HeaderVisitor.php | 50 - .../LocationVisitor/Response/JsonVisitor.php | 93 - .../Response/ReasonPhraseVisitor.php | 23 - .../Response/ResponseVisitorInterface.php | 46 - .../Response/StatusCodeVisitor.php | 23 - .../LocationVisitor/Response/XmlVisitor.php | 151 - .../LocationVisitor/VisitorFlyweight.php | 138 - .../Service/Command/OperationCommand.php | 89 - .../Command/OperationResponseParser.php | 195 - .../Command/RequestSerializerInterface.php | 21 - .../Command/ResponseClassInterface.php | 18 - .../Command/ResponseParserInterface.php | 18 - .../Guzzle/Service/ConfigLoaderInterface.php | 22 - .../Guzzle/Service/Description/Operation.php | 547 -- .../Description/OperationInterface.php | 159 - .../Guzzle/Service/Description/Parameter.php | 925 -- .../Service/Description/SchemaFormatter.php | 156 - .../Service/Description/SchemaValidator.php | 291 - .../Description/ServiceDescription.php | 271 - .../ServiceDescriptionInterface.php | 106 - .../Description/ServiceDescriptionLoader.php | 64 - .../Description/ValidatorInterface.php | 28 - .../Service/Exception/CommandException.php | 7 - .../Exception/CommandTransferException.php | 119 - .../Exception/DescriptionBuilderException.php | 7 - .../InconsistentClientTransferException.php | 38 - .../Exception/ResponseClassException.php | 9 - .../Exception/ServiceBuilderException.php | 7 - .../Exception/ServiceNotFoundException.php | 5 - .../Service/Exception/ValidationException.php | 30 - .../AbstractResourceIteratorFactory.php | 37 - .../CompositeResourceIteratorFactory.php | 67 - .../Resource/MapResourceIteratorFactory.php | 34 - .../src/Guzzle/Service/Resource/Model.php | 64 - .../Service/Resource/ResourceIterator.php | 254 - .../Resource/ResourceIteratorApplyBatched.php | 111 - .../Resource/ResourceIteratorClassFactory.php | 60 - .../ResourceIteratorFactoryInterface.php | 30 - .../Resource/ResourceIteratorInterface.php | 61 - .../guzzle/src/Guzzle/Service/composer.json | 29 - .../Guzzle/Stream/PhpStreamRequestFactory.php | 276 - .../guzzle/src/Guzzle/Stream/Stream.php | 289 - .../src/Guzzle/Stream/StreamInterface.php | 218 - .../Stream/StreamRequestFactoryInterface.php | 24 - .../guzzle/src/Guzzle/Stream/composer.json | 30 - core/lib/michelf/php-markdown/License.md | 36 - .../php-markdown/Michelf/Markdown.inc.php | 10 - .../michelf/php-markdown/Michelf/Markdown.php | 3106 ------ .../Michelf/MarkdownExtra.inc.php | 11 - .../php-markdown/Michelf/MarkdownExtra.php | 38 - .../Michelf/MarkdownInterface.inc.php | 9 - .../Michelf/MarkdownInterface.php | 37 - core/lib/michelf/php-markdown/Readme.md | 271 - core/lib/michelf/php-markdown/Readme.php | 31 - core/lib/michelf/php-markdown/composer.json | 31 - core/lib/pimple/pimple/.gitignore | 1 - core/lib/pimple/pimple/.travis.yml | 6 - core/lib/pimple/pimple/LICENSE | 19 - core/lib/pimple/pimple/README.rst | 159 - core/lib/pimple/pimple/composer.json | 25 - core/lib/pimple/pimple/lib/Pimple.php | 214 - core/lib/pimple/pimple/phpunit.xml.dist | 19 - core/lib/scan/kss-php/.editorconfig | 13 - core/lib/scan/kss-php/.gitignore | 3 - core/lib/scan/kss-php/ACKNOWLEDGEMENTS | 23 - core/lib/scan/kss-php/CONTRIBUTING.md | 41 - core/lib/scan/kss-php/LICENSE | 20 - core/lib/scan/kss-php/README.md | 125 - core/lib/scan/kss-php/build.xml | 92 - core/lib/scan/kss-php/composer.json | 29 - core/lib/scan/kss-php/composer.lock | 475 - .../kss-php/lib/Scan/Kss/CommentParser.php | 226 - .../Scan/Kss/Exception/ExceptionInterface.php | 7 - .../Exception/UnexpectedValueException.php | 7 - .../scan/kss-php/lib/Scan/Kss/Modifier.php | 236 - core/lib/scan/kss-php/lib/Scan/Kss/Parser.php | 196 - .../lib/scan/kss-php/lib/Scan/Kss/Section.php | 518 - core/lib/scan/kss-php/lib/Scan/kss.coffee | 41 - core/lib/scan/kss-php/phpunit.xml.dist | 28 - .../Component/EventDispatcher/.gitignore | 3 - .../Component/EventDispatcher/CHANGELOG.md | 16 - .../ContainerAwareEventDispatcher.php | 202 - .../TraceableEventDispatcherInterface.php | 32 - .../Component/EventDispatcher/Event.php | 129 - .../EventDispatcher/EventDispatcher.php | 185 - .../EventDispatcherInterface.php | 96 - .../EventSubscriberInterface.php | 50 - .../EventDispatcher/GenericEvent.php | 186 - .../ImmutableEventDispatcher.php | 92 - .../Symfony/Component/EventDispatcher/LICENSE | 19 - .../Component/EventDispatcher/README.md | 25 - .../ContainerAwareEventDispatcherTest.php | 244 - .../Tests/EventDispatcherTest.php | 346 - .../EventDispatcher/Tests/EventTest.php | 84 - .../Tests/GenericEventTest.php | 140 - .../Tests/ImmutableEventDispatcherTest.php | 105 - .../Component/EventDispatcher/composer.json | 38 - .../EventDispatcher/phpunit.xml.dist | 30 - .../Symfony/Component/Filesystem/.gitignore | 3 - .../Symfony/Component/Filesystem/CHANGELOG.md | 23 - .../Exception/ExceptionInterface.php | 23 - .../Exception/FileNotFoundException.php | 34 - .../Filesystem/Exception/IOException.php | 41 - .../Exception/IOExceptionInterface.php | 27 - .../Component/Filesystem/Filesystem.php | 477 - .../Symfony/Component/Filesystem/LICENSE | 19 - .../Symfony/Component/Filesystem/README.md | 45 - .../Filesystem/Tests/ExceptionTest.php | 46 - .../Filesystem/Tests/FilesystemTest.php | 907 -- .../Filesystem/Tests/FilesystemTestCase.php | 125 - .../Component/Filesystem/composer.json | 31 - .../Component/Filesystem/phpunit.xml.dist | 28 - .../Symfony/Component/Finder/.gitignore | 3 - .../Finder/Adapter/AbstractAdapter.php | 236 - .../Finder/Adapter/AbstractFindAdapter.php | 327 - .../Finder/Adapter/AdapterInterface.php | 144 - .../Finder/Adapter/BsdFindAdapter.php | 103 - .../Finder/Adapter/GnuFindAdapter.php | 104 - .../Component/Finder/Adapter/PhpAdapter.php | 98 - .../Symfony/Component/Finder/CHANGELOG.md | 30 - .../Finder/Comparator/Comparator.php | 98 - .../Finder/Comparator/DateComparator.php | 54 - .../Finder/Comparator/NumberComparator.php | 82 - .../Exception/AccessDeniedException.php | 19 - .../Exception/AdapterFailureException.php | 46 - .../Finder/Exception/ExceptionInterface.php | 23 - .../OperationNotPermitedException.php | 19 - .../ShellCommandFailureException.php | 45 - .../Finder/Expression/Expression.php | 146 - .../Component/Finder/Expression/Glob.php | 157 - .../Component/Finder/Expression/Regex.php | 321 - .../Finder/Expression/ValueInterface.php | 60 - .../Symfony/Component/Finder/Finder.php | 829 -- .../finder/Symfony/Component/Finder/Glob.php | 103 - .../Finder/Iterator/CustomFilterIterator.php | 63 - .../Iterator/DateRangeFilterIterator.php | 60 - .../Iterator/DepthRangeFilterIterator.php | 47 - .../ExcludeDirectoryFilterIterator.php | 55 - .../Finder/Iterator/FilePathsIterator.php | 131 - .../Iterator/FileTypeFilterIterator.php | 55 - .../Iterator/FilecontentFilterIterator.php | 76 - .../Iterator/FilenameFilterIterator.php | 68 - .../Finder/Iterator/FilterIterator.php | 49 - .../Iterator/MultiplePcreFilterIterator.php | 66 - .../Finder/Iterator/PathFilterIterator.php | 75 - .../Iterator/RecursiveDirectoryIterator.php | 126 - .../Iterator/SizeRangeFilterIterator.php | 59 - .../Finder/Iterator/SortableIterator.php | 82 - .../finder/Symfony/Component/Finder/LICENSE | 19 - .../finder/Symfony/Component/Finder/README.md | 41 - .../Component/Finder/Shell/Command.php | 294 - .../Symfony/Component/Finder/Shell/Shell.php | 95 - .../Symfony/Component/Finder/SplFileInfo.php | 77 - .../Symfony/Component/Finder/composer.json | 31 - .../Symfony/Component/Finder/phpunit.xml.dist | 29 - .../Symfony/Component/Process/.gitignore | 3 - .../Symfony/Component/Process/CHANGELOG.md | 31 - .../Process/Exception/ExceptionInterface.php | 21 - .../Exception/InvalidArgumentException.php | 21 - .../Process/Exception/LogicException.php | 21 - .../Exception/ProcessFailedException.php | 49 - .../Exception/ProcessTimedOutException.php | 69 - .../Process/Exception/RuntimeException.php | 21 - .../Component/Process/ExecutableFinder.php | 90 - .../process/Symfony/Component/Process/LICENSE | 19 - .../Component/Process/PhpExecutableFinder.php | 67 - .../Symfony/Component/Process/PhpProcess.php | 73 - .../Symfony/Component/Process/Process.php | 1312 --- .../Component/Process/ProcessBuilder.php | 241 - .../Component/Process/ProcessPipes.php | 358 - .../Component/Process/ProcessUtils.php | 79 - .../Symfony/Component/Process/README.md | 47 - .../Process/Tests/AbstractProcessTest.php | 873 -- .../Process/Tests/NonStopableProcess.php | 37 - .../Process/Tests/PhpExecutableFinderTest.php | 64 - .../Process/Tests/PhpProcessTest.php | 29 - .../PipeStdinInStdoutStdErrStreamSelect.php | 63 - .../Process/Tests/ProcessBuilderTest.php | 196 - .../Tests/ProcessFailedExceptionTest.php | 83 - .../Tests/ProcessInSigchildEnvironment.php | 22 - .../Process/Tests/ProcessUtilsTest.php | 48 - .../Tests/SigchildDisabledProcessTest.php | 224 - .../Tests/SigchildEnabledProcessTest.php | 133 - .../Process/Tests/SignalListener.php | 16 - .../Process/Tests/SimpleProcessTest.php | 171 - .../Symfony/Component/Process/composer.json | 31 - .../Component/Process/phpunit.xml.dist | 28 - .../yaml/Symfony/Component/Yaml/.gitignore | 3 - .../yaml/Symfony/Component/Yaml/CHANGELOG.md | 8 - .../yaml/Symfony/Component/Yaml/Dumper.php | 73 - .../yaml/Symfony/Component/Yaml/Escaper.php | 89 - .../Yaml/Exception/DumpException.php | 23 - .../Yaml/Exception/ExceptionInterface.php | 23 - .../Yaml/Exception/ParseException.php | 148 - .../Yaml/Exception/RuntimeException.php | 23 - .../yaml/Symfony/Component/Yaml/Inline.php | 469 - .../yaml/Symfony/Component/Yaml/LICENSE | 19 - .../yaml/Symfony/Component/Yaml/Parser.php | 638 -- .../yaml/Symfony/Component/Yaml/README.md | 19 - .../Component/Yaml/Tests/DumperTest.php | 207 - .../Yaml/Tests/Fixtures/YtsAnchorAlias.yml | 31 - .../Yaml/Tests/Fixtures/YtsBasicTests.yml | 178 - .../Yaml/Tests/Fixtures/YtsBlockMapping.yml | 51 - .../Tests/Fixtures/YtsDocumentSeparator.yml | 85 - .../Yaml/Tests/Fixtures/YtsErrorTests.yml | 25 - .../Tests/Fixtures/YtsFlowCollections.yml | 60 - .../Yaml/Tests/Fixtures/YtsFoldedScalars.yml | 176 - .../Tests/Fixtures/YtsNullsAndEmpties.yml | 45 - .../Fixtures/YtsSpecificationExamples.yml | 1695 ---- .../Yaml/Tests/Fixtures/YtsTypeTransfers.yml | 244 - .../Yaml/Tests/Fixtures/embededPhp.yml | 1 - .../Yaml/Tests/Fixtures/escapedCharacters.yml | 147 - .../Component/Yaml/Tests/Fixtures/index.yml | 18 - .../Yaml/Tests/Fixtures/sfComments.yml | 65 - .../Yaml/Tests/Fixtures/sfCompact.yml | 159 - .../Yaml/Tests/Fixtures/sfMergeKey.yml | 27 - .../Yaml/Tests/Fixtures/sfObjects.yml | 11 - .../Yaml/Tests/Fixtures/sfQuotes.yml | 33 - .../Component/Yaml/Tests/Fixtures/sfTests.yml | 135 - .../Tests/Fixtures/unindentedCollections.yml | 62 - .../Component/Yaml/Tests/InlineTest.php | 231 - .../Yaml/Tests/ParseExceptionTest.php | 30 - .../Component/Yaml/Tests/ParserTest.php | 619 -- .../Symfony/Component/Yaml/Tests/YamlTest.php | 31 - .../yaml/Symfony/Component/Yaml/Unescaper.php | 146 - .../yaml/Symfony/Component/Yaml/Yaml.php | 100 - .../yaml/Symfony/Component/Yaml/composer.json | 31 - .../Symfony/Component/Yaml/phpunit.xml.dist | 29 - core/migrations/001-move-source.json | 5 - .../002-check-public-styleguide-exists.json | 5 - core/migrations/003-move-styleguide.json | 5 - core/migrations/004-check-meta-exists.json | 5 - .../005-move-pattern-header-footer.json | 5 - .../006-check-public-data-exists.json | 5 - .../007-check-public-patterns-exists.json | 5 - core/pageFollowServer.php | 40 - core/scripts/README | 6 - core/scripts/generateSite.command | 3 - core/scripts/generateSiteWithCSS.command | 3 - core/scripts/startAutoReloadServer.command | 3 - core/scripts/startPageFollowServer.command | 3 - core/scripts/startWatcher.command | 3 - .../startWatcherWithAutoReload.command | 3 - core/source/_annotations/annotations.js | 34 - core/source/_data/data.json | 93 - core/source/_data/listitems.json | 878 -- core/source/_meta/_00-head.mustache | 16 - core/source/_meta/_01-foot.mustache | 6 - .../00-atoms/01-global/00-colors.mustache | 38 - .../00-atoms/01-global/01-fonts.mustache | 6 - .../00-atoms/01-global/02-animations.mustache | 3 - .../00-atoms/01-global/03-visibility.mustache | 11 - .../00-atoms/02-text/00-headings.mustache | 6 - .../00-atoms/02-text/01-paragraph.mustache | 1 - .../00-atoms/02-text/02-blockquote.mustache | 3 - .../02-text/03-inline-elements.mustache | 41 - .../00-atoms/02-text/04-time.mustache | 1 - .../02-text/05-preformatted-text.mustache | 9 - .../_patterns/00-atoms/02-text/06-hr.mustache | 1 - .../00-atoms/03-lists/00-unordered.mustache | 14 - .../00-atoms/03-lists/01-ordered.mustache | 14 - .../00-atoms/03-lists/02-definition.mustache | 10 - .../00-atoms/04-images/00-logo.mustache | 1 - .../04-images/01-landscape-4x3.mustache | 1 - .../04-images/02-landscape-16x9.mustache | 1 - .../00-atoms/04-images/03-square.mustache | 1 - .../00-atoms/04-images/04-avatar.mustache | 1 - .../00-atoms/04-images/05-icons.mustache | 12 - .../04-images/06-loading-icon.mustache | 1 - .../00-atoms/04-images/07-favicon.mustache | 2 - .../00-atoms/05-forms/00-text-fields.mustache | 38 - .../00-atoms/05-forms/01-select-menu.mustache | 12 - .../00-atoms/05-forms/02-checkbox.mustache | 10 - .../05-forms/03-radio-buttons.mustache | 10 - .../05-forms/04-html5-inputs.mustache | 10 - .../00-atoms/06-buttons/00-buttons.mustache | 4 - .../00-atoms/07-tables/00-table.mustache | 50 - .../00-atoms/08-media/_00-video.mustache | 6 - .../00-atoms/08-media/_01-audio.mustache | 4 - .../01-molecules/00-text/00-byline.mustache | 1 - .../01-molecules/00-text/01-address.mustache | 11 - .../00-text/02-heading-group.mustache | 4 - .../03-blockquote-with-citation.mustache | 4 - .../00-text/04-intro-text.mustache | 1 - .../01-molecules/01-layout/00-one-up.mustache | 7 - .../01-molecules/01-layout/01-two-up.mustache | 7 - .../01-layout/02-three-up.mustache | 8 - .../01-layout/03-four-up.mustache | 9 - .../02-blocks/00-media-block.mustache | 11 - .../01-block-headline-byline.mustache | 6 - .../02-blocks/02-block-hero.mustache | 10 - .../03-block-thumb-headline.mustache | 10 - .../02-blocks/04-block-headline.mustache | 5 - .../02-blocks/05-block-inset.mustache | 10 - .../03-media/00-figure-with-caption.mustache | 4 - .../01-molecules/04-forms/00-search.mustache | 11 - .../04-forms/01-comment-form.mustache | 20 - .../04-forms/02-newsletter.mustache | 10 - .../05-navigation/00-primary-nav.mustache | 8 - .../05-navigation/01-footer-nav.mustache | 6 - .../05-navigation/02-breadcrumbs.mustache | 7 - .../05-navigation/03-pagination.mustache | 9 - .../05-navigation/04-tabs.mustache | 7 - .../06-components/00-social-share.mustache | 8 - .../06-components/01-accordion.mustache | 19 - .../06-components/02-single-comment.mustache | 9 - .../07-messaging/00-alert.mustache | 3 - .../02-organisms/00-global/00-header.mustache | 9 - .../02-organisms/00-global/01-footer.mustache | 7 - .../01-article/00-article-body.mustache | 22 - .../02-comments/00-comment-thread.mustache | 12 - .../03-sections/00-latest-posts.mustache | 9 - .../03-sections/01-recent-tweets.mustache | 12 - .../03-sections/02-related-posts.mustache | 10 - .../03-templates/00-homepage.mustache | 40 - .../_patterns/03-templates/01-blog.mustache | 17 - .../03-templates/02-article.mustache | 24 - .../_patterns/04-pages/00-homepage.json | 135 - .../_patterns/04-pages/00-homepage.mustache | 1 - .../04-pages/00-homepage~emergency.json | 8 - core/source/_patterns/04-pages/01-blog.json | 86 - .../_patterns/04-pages/01-blog.mustache | 1 - .../source/_patterns/04-pages/02-article.json | 28 - .../_patterns/04-pages/02-article.mustache | 1 - core/source/css/scss/base/_animation.scss | 13 - core/source/css/scss/base/_forms.scss | 111 - .../source/css/scss/base/_global-classes.scss | 104 - core/source/css/scss/base/_headings.scss | 31 - core/source/css/scss/base/_links.scss | 10 - core/source/css/scss/base/_lists.scss | 19 - core/source/css/scss/base/_main.scss | 6 - core/source/css/scss/base/_media.scss | 23 - core/source/css/scss/base/_tables.scss | 18 - core/source/css/scss/base/_text.scss | 26 - core/source/css/scss/generic/_mixins.scss | 23 - core/source/css/scss/generic/_reset.scss | 17 - core/source/css/scss/generic/_variables.scss | 65 - core/source/css/scss/objects/_accordion.scss | 33 - core/source/css/scss/objects/_article.scss | 34 - core/source/css/scss/objects/_blocks.scss | 138 - core/source/css/scss/objects/_buttons.scss | 40 - core/source/css/scss/objects/_carousels.scss | 38 - core/source/css/scss/objects/_comments.scss | 30 - core/source/css/scss/objects/_footer.scss | 39 - core/source/css/scss/objects/_header.scss | 45 - core/source/css/scss/objects/_icons.scss | 125 - core/source/css/scss/objects/_layout.scss | 288 - core/source/css/scss/objects/_lists.scss | 63 - core/source/css/scss/objects/_main.scss | 9 - core/source/css/scss/objects/_messaging.scss | 18 - core/source/css/scss/objects/_nav.scss | 52 - core/source/css/scss/objects/_sections.scss | 7 - core/source/css/scss/objects/_tabs.scss | 33 - core/source/css/scss/objects/_text.scss | 41 - core/source/css/scss/objects/_tooltip.scss | 42 - core/source/css/style.css | 1103 --- core/source/css/style.scss | 121 - core/source/favicon.ico | Bin 32988 -> 0 bytes core/source/fonts/icons.dev.svg | 103 - core/source/fonts/icons.eot | Bin 4372 -> 0 bytes core/source/fonts/icons.svg | 103 - core/source/fonts/icons.ttf | Bin 4216 -> 0 bytes core/source/fonts/icons.woff | Bin 6176 -> 0 bytes core/source/images/ajax-loader.gif | Bin 673 -> 0 bytes core/source/images/favicon_16x16.jpg | Bin 311 -> 0 bytes core/source/images/favicon_32x32.jpg | Bin 320 -> 0 bytes core/source/images/fpo_16x9.png | Bin 8416 -> 0 bytes core/source/images/fpo_4x3.png | Bin 3709 -> 0 bytes core/source/images/fpo_avatar.png | Bin 3217 -> 0 bytes core/source/images/fpo_square.png | Bin 8694 -> 0 bytes core/source/images/logo.png | Bin 1931 -> 0 bytes .../sample/landscape-16x9-mountains.jpg | Bin 78249 -> 0 bytes .../images/sample/thumb-square-fire.jpg | Bin 72783 -> 0 bytes .../images/sample/thumb-square-gear.jpg | Bin 146848 -> 0 bytes .../source/images/sample/thumb-square-ivy.jpg | Bin 76319 -> 0 bytes .../images/sample/thumb-square-river.jpg | Bin 50522 -> 0 bytes .../images/sample/thumb-square-yosemite.jpg | Bin 84720 -> 0 bytes .../source/images/sample/tout-4x3-climber.jpg | Bin 114565 -> 0 bytes .../images/sample/tout-4x3-climbers.jpg | Bin 85295 -> 0 bytes core/source/images/sample/tout-4x3-stream.jpg | Bin 98049 -> 0 bytes core/source/js/fitvids.js | 77 - core/source/js/init.js | 26 - core/source/js/jquery-2.0.0b2.js | 8690 ----------------- core/source/js/modernizr.js | 4 - core/styleguide/css/styleguide-specific.css | 80 - core/styleguide/css/styleguide-specific.scss | 141 - core/styleguide/css/styleguide.css | 703 -- core/styleguide/css/styleguide.scss | 953 -- core/styleguide/css/vendor/prism.css | 112 - core/styleguide/css/vendor/typeahead.css | 66 - core/styleguide/fonts/icomoon.eot | Bin 3872 -> 0 bytes core/styleguide/fonts/icomoon.svg | 22 - core/styleguide/fonts/icomoon.ttf | Bin 3708 -> 0 bytes core/styleguide/fonts/icomoon.woff | Bin 3144 -> 0 bytes core/styleguide/html/README | 1 - core/styleguide/images/spinner.gif | Bin 23470 -> 0 bytes core/styleguide/js/annotations-pattern.js | 308 - core/styleguide/js/annotations-viewer.js | 289 - core/styleguide/js/code-pattern.js | 120 - core/styleguide/js/code-viewer.js | 443 - core/styleguide/js/data-saver.js | 156 - core/styleguide/js/pattern-finder.js | 122 - core/styleguide/js/postmessage.js | 120 - core/styleguide/js/qrcode-generator.js | 93 - core/styleguide/js/styleguide.js | 626 -- core/styleguide/js/synclisteners.js | 176 - core/styleguide/js/url-handler.js | 193 - .../js/vendor/classlist-polyfill.js | 176 - core/styleguide/js/vendor/jquery.js | 4 - core/styleguide/js/vendor/jwerty.js | 523 - core/styleguide/js/vendor/prism.js | 7 - .../js/vendor/typeahead.bundle.min.js | 7 - core/templates/README | 1 - core/templates/index.mustache | 73 - core/templates/partials/ipAddress.mustache | 7 - core/templates/partials/ishControls.mustache | 66 - core/templates/partials/patternNav.mustache | 17 - core/templates/partials/patternPaths.mustache | 3 - .../partials/patternSection.mustache | 91 - .../partials/patternSectionSubtype.mustache | 6 - core/templates/partials/viewAllPaths.mustache | 3 - .../partials/viewerAnnotation.mustache | 4 - core/templates/partials/viewerCode.mustache | 32 - core/templates/partials/websockets.mustache | 8 - core/templates/pattern-header-footer/README | 1 - .../pattern-header-footer/footer-pattern.html | 15 - .../pattern-header-footer/footer.html | 54 - .../pattern-header-footer/header.html | 10 - core/templates/snapshot.mustache | 17 - core/templates/viewall.mustache | 25 - extras/apache/README | 37 - extras/apache/vhost.txt | 5 - 911 files changed, 104927 deletions(-) delete mode 100644 composer.json delete mode 100644 composer.lock delete mode 100755 core/autoReloadServer.php delete mode 100644 core/builder.php delete mode 100644 core/config/config.ini.default delete mode 100644 core/lib/CSSRuleSaver/CSSRuleSaver.php delete mode 100644 core/lib/CSSRuleSaver/LICENSE delete mode 100644 core/lib/CSSRuleSaver/LICENSE.SelectorDOM delete mode 100755 core/lib/CSSRuleSaver/SelectorDOM.php delete mode 100644 core/lib/Mustache/Autoloader.php delete mode 100644 core/lib/Mustache/Compiler.php delete mode 100644 core/lib/Mustache/Context.php delete mode 100644 core/lib/Mustache/Engine.php delete mode 100644 core/lib/Mustache/Exception.php delete mode 100644 core/lib/Mustache/Exception/InvalidArgumentException.php delete mode 100644 core/lib/Mustache/Exception/LogicException.php delete mode 100644 core/lib/Mustache/Exception/RuntimeException.php delete mode 100644 core/lib/Mustache/Exception/SyntaxException.php delete mode 100644 core/lib/Mustache/Exception/UnknownFilterException.php delete mode 100644 core/lib/Mustache/Exception/UnknownHelperException.php delete mode 100644 core/lib/Mustache/Exception/UnknownTemplateException.php delete mode 100644 core/lib/Mustache/HelperCollection.php delete mode 100644 core/lib/Mustache/LICENSE delete mode 100644 core/lib/Mustache/LambdaHelper.php delete mode 100644 core/lib/Mustache/Loader.php delete mode 100644 core/lib/Mustache/Loader/ArrayLoader.php delete mode 100644 core/lib/Mustache/Loader/CascadingLoader.php delete mode 100644 core/lib/Mustache/Loader/FilesystemLoader.php delete mode 100644 core/lib/Mustache/Loader/InlineLoader.php delete mode 100644 core/lib/Mustache/Loader/MutableLoader.php delete mode 100644 core/lib/Mustache/Loader/StringLoader.php delete mode 100644 core/lib/Mustache/Logger.php delete mode 100644 core/lib/Mustache/Logger/AbstractLogger.php delete mode 100644 core/lib/Mustache/Logger/StreamLogger.php delete mode 100644 core/lib/Mustache/Parser.php delete mode 100644 core/lib/Mustache/Template.php delete mode 100644 core/lib/Mustache/Tokenizer.php delete mode 100644 core/lib/PatternLab/Builder.php delete mode 100644 core/lib/PatternLab/Config.php delete mode 100644 core/lib/PatternLab/Console.php delete mode 100644 core/lib/PatternLab/Data.php delete mode 100644 core/lib/PatternLab/FileUtil.php delete mode 100644 core/lib/PatternLab/Generator.php delete mode 100644 core/lib/PatternLab/JSON.php delete mode 100644 core/lib/PatternLab/Migrator.php delete mode 100644 core/lib/PatternLab/Parsers/Documentation.php delete mode 100644 core/lib/PatternLab/Parsers/Plugins/KSS.php delete mode 100644 core/lib/PatternLab/PatternData.php delete mode 100644 core/lib/PatternLab/PatternData/Exporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/DataLinkExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php delete mode 100644 core/lib/PatternLab/PatternData/Helper.php delete mode 100644 core/lib/PatternLab/PatternData/Helpers/LineageHelper.php delete mode 100644 core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php delete mode 100644 core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php delete mode 100644 core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php delete mode 100644 core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php delete mode 100644 core/lib/PatternLab/PatternData/Rule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/DocumentationRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PatternRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php delete mode 100644 core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php delete mode 100644 core/lib/PatternLab/PatternEngine.php delete mode 100644 core/lib/PatternLab/PatternEngine/Loader.php delete mode 100644 core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php delete mode 100644 core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php delete mode 100644 core/lib/PatternLab/PatternEngine/Rule.php delete mode 100644 core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php delete mode 100644 core/lib/PatternLab/PatternEngine/Rules/TwigRule.php delete mode 100644 core/lib/PatternLab/Render.php delete mode 100644 core/lib/PatternLab/Snapshot.php delete mode 100644 core/lib/PatternLab/Snapshot/FilterIterator.php delete mode 100644 core/lib/PatternLab/StarterKit.php delete mode 100644 core/lib/PatternLab/Template/Helper.php delete mode 100644 core/lib/PatternLab/Template/Loader.php delete mode 100644 core/lib/PatternLab/Util.php delete mode 100644 core/lib/PatternLab/Watcher.php delete mode 100755 core/lib/Seld/JsonLint/JsonParser.php delete mode 100755 core/lib/Seld/JsonLint/LICENSE delete mode 100755 core/lib/Seld/JsonLint/Lexer.php delete mode 100755 core/lib/Seld/JsonLint/ParsingException.php delete mode 100755 core/lib/Seld/JsonLint/Undefined.php delete mode 100755 core/lib/Wrench/Application/Application.php delete mode 100644 core/lib/Wrench/Application/AutoReloadApplication.php delete mode 100644 core/lib/Wrench/Application/PageFollowApplication.php delete mode 100755 core/lib/Wrench/BasicServer.php delete mode 100755 core/lib/Wrench/Client.php delete mode 100755 core/lib/Wrench/Connection.php delete mode 100755 core/lib/Wrench/ConnectionManager.php delete mode 100755 core/lib/Wrench/Exception/BadRequestException.php delete mode 100755 core/lib/Wrench/Exception/CloseException.php delete mode 100755 core/lib/Wrench/Exception/ConnectionException.php delete mode 100755 core/lib/Wrench/Exception/Exception.php delete mode 100755 core/lib/Wrench/Exception/FrameException.php delete mode 100755 core/lib/Wrench/Exception/HandshakeException.php delete mode 100755 core/lib/Wrench/Exception/InvalidOriginException.php delete mode 100755 core/lib/Wrench/Exception/PayloadException.php delete mode 100755 core/lib/Wrench/Exception/RateLimiterException.php delete mode 100755 core/lib/Wrench/Exception/SocketException.php delete mode 100755 core/lib/Wrench/Frame/Frame.php delete mode 100755 core/lib/Wrench/Frame/HybiFrame.php delete mode 100755 core/lib/Wrench/Listener/HandshakeRequestListener.php delete mode 100755 core/lib/Wrench/Listener/Listener.php delete mode 100755 core/lib/Wrench/Listener/OriginPolicy.php delete mode 100755 core/lib/Wrench/Listener/RateLimiter.php delete mode 100755 core/lib/Wrench/Payload/HybiPayload.php delete mode 100755 core/lib/Wrench/Payload/Payload.php delete mode 100755 core/lib/Wrench/Payload/PayloadHandler.php delete mode 100755 core/lib/Wrench/Protocol/Hybi10Protocol.php delete mode 100755 core/lib/Wrench/Protocol/HybiProtocol.php delete mode 100755 core/lib/Wrench/Protocol/Protocol.php delete mode 100755 core/lib/Wrench/Protocol/Rfc6455Protocol.php delete mode 100755 core/lib/Wrench/Resource.php delete mode 100755 core/lib/Wrench/Server.php delete mode 100755 core/lib/Wrench/Socket/ClientSocket.php delete mode 100755 core/lib/Wrench/Socket/ServerClientSocket.php delete mode 100755 core/lib/Wrench/Socket/ServerSocket.php delete mode 100755 core/lib/Wrench/Socket/Socket.php delete mode 100755 core/lib/Wrench/Socket/UriSocket.php delete mode 100755 core/lib/Wrench/Tests/Application/EchoApplicationTest.php delete mode 100755 core/lib/Wrench/Tests/BasicServerTest.php delete mode 100755 core/lib/Wrench/Tests/ClientTest.php delete mode 100755 core/lib/Wrench/Tests/ConnectionManagerTest.php delete mode 100755 core/lib/Wrench/Tests/ConnectionTest.php delete mode 100755 core/lib/Wrench/Tests/Frame/BaseSubclassFrameTest.php delete mode 100755 core/lib/Wrench/Tests/Frame/FrameTest.php delete mode 100755 core/lib/Wrench/Tests/Frame/HybiFrameTest.php delete mode 100755 core/lib/Wrench/Tests/Listener/ListenerTest.php delete mode 100755 core/lib/Wrench/Tests/Listener/OriginPolicyTest.php delete mode 100755 core/lib/Wrench/Tests/Listener/RateLimiterTest.php delete mode 100755 core/lib/Wrench/Tests/Payload/HybiPayloadTest.php delete mode 100755 core/lib/Wrench/Tests/Payload/PayloadTest.php delete mode 100755 core/lib/Wrench/Tests/Protocol/ProtocolTest.php delete mode 100755 core/lib/Wrench/Tests/Protocol/Rfc6455ProtocolTest.php delete mode 100755 core/lib/Wrench/Tests/ServerTest.php delete mode 100755 core/lib/Wrench/Tests/ServerTestHelper.php delete mode 100755 core/lib/Wrench/Tests/Socket/ClientSocketTest.php delete mode 100755 core/lib/Wrench/Tests/Socket/ServerClientSocketTest.php delete mode 100755 core/lib/Wrench/Tests/Socket/ServerSocketTest.php delete mode 100755 core/lib/Wrench/Tests/Socket/SocketTest.php delete mode 100755 core/lib/Wrench/Tests/Socket/UriSocketTest.php delete mode 100755 core/lib/Wrench/Tests/Test.php delete mode 100755 core/lib/Wrench/Tests/bootstrap.php delete mode 100755 core/lib/Wrench/Tests/server.php delete mode 100755 core/lib/Wrench/Util/Configurable.php delete mode 100755 core/lib/Wrench/Util/Ssl.php delete mode 100644 core/lib/alchemy/zippy/.gitignore delete mode 100644 core/lib/alchemy/zippy/.travis.yml delete mode 100644 core/lib/alchemy/zippy/CHANGELOG.md delete mode 100644 core/lib/alchemy/zippy/LICENSE delete mode 100644 core/lib/alchemy/zippy/README.md delete mode 100644 core/lib/alchemy/zippy/composer.json delete mode 100644 core/lib/alchemy/zippy/composer.lock delete mode 100644 core/lib/alchemy/zippy/phpunit-functional.xml.dist delete mode 100644 core/lib/alchemy/zippy/phpunit.xml.dist delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarGzBSDTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BinaryAdapterInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGNUTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/FileResource.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php delete mode 100644 core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php delete mode 100644 core/lib/autoload.php delete mode 100644 core/lib/composer/ClassLoader.php delete mode 100644 core/lib/composer/autoload_classmap.php delete mode 100644 core/lib/composer/autoload_namespaces.php delete mode 100644 core/lib/composer/autoload_psr4.php delete mode 100644 core/lib/composer/autoload_real.php delete mode 100644 core/lib/composer/installed.json delete mode 100644 core/lib/doctrine/collections/.gitignore delete mode 100644 core/lib/doctrine/collections/.travis.yml delete mode 100644 core/lib/doctrine/collections/LICENSE delete mode 100644 core/lib/doctrine/collections/README.md delete mode 100644 core/lib/doctrine/collections/composer.json delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php delete mode 100644 core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php delete mode 100644 core/lib/doctrine/collections/phpunit.xml.dist delete mode 100644 core/lib/guzzle/guzzle/.gitignore delete mode 100644 core/lib/guzzle/guzzle/.travis.yml delete mode 100644 core/lib/guzzle/guzzle/CHANGELOG.md delete mode 100644 core/lib/guzzle/guzzle/LICENSE delete mode 100644 core/lib/guzzle/guzzle/README.md delete mode 100644 core/lib/guzzle/guzzle/UPGRADING.md delete mode 100644 core/lib/guzzle/guzzle/build.xml delete mode 100644 core/lib/guzzle/guzzle/composer.json delete mode 100644 core/lib/guzzle/guzzle/phar-stub.php delete mode 100644 core/lib/guzzle/guzzle/phing/build.properties.dist delete mode 100644 core/lib/guzzle/guzzle/phing/imports/dependencies.xml delete mode 100644 core/lib/guzzle/guzzle/phing/imports/deploy.xml delete mode 100644 core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php delete mode 100644 core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php delete mode 100644 core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php delete mode 100644 core/lib/guzzle/guzzle/phpunit.xml.dist delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchRequestTransfer.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/Exception/BatchTransferException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/ClosureCacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf1CacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/RuntimeException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/UnexpectedValueException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/FromConfigInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/HasDispatcherInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/ToArrayInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/Version.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Common/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiProxy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/CouldNotRewindStreamException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/CurlException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/MultiTransferException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/TooManyRedirectsException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/IoEmittingEntityBody.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/Link.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFile.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Request.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Response.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/QueryString.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/InflectorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/MemoizingInflector.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/MessageFormatter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/AbstractMessageParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/PeclHttpMessageParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Parser/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CallbackBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/HttpBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CachePlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CallbackCanCacheStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheKeyProvider.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/RevalidationInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/SkipRevalidation.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/FileCookieJar.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponsePlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderLoader.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/AbstractCommand.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CreateResponseClassEvent.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/MapFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/ResponseBodyVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/BodyVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/StatusCodeVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ResponseClassInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ResponseParserInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/ConfigLoaderInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/Operation.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/Parameter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionLoader.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/CommandTransferException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/InconsistentClientTransferException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceBuilderException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ServiceNotFoundException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ValidationException.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Service/composer.json delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamRequestFactoryInterface.php delete mode 100644 core/lib/guzzle/guzzle/src/Guzzle/Stream/composer.json delete mode 100644 core/lib/michelf/php-markdown/License.md delete mode 100644 core/lib/michelf/php-markdown/Michelf/Markdown.inc.php delete mode 100644 core/lib/michelf/php-markdown/Michelf/Markdown.php delete mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php delete mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownExtra.php delete mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php delete mode 100644 core/lib/michelf/php-markdown/Michelf/MarkdownInterface.php delete mode 100644 core/lib/michelf/php-markdown/Readme.md delete mode 100644 core/lib/michelf/php-markdown/Readme.php delete mode 100644 core/lib/michelf/php-markdown/composer.json delete mode 100644 core/lib/pimple/pimple/.gitignore delete mode 100644 core/lib/pimple/pimple/.travis.yml delete mode 100644 core/lib/pimple/pimple/LICENSE delete mode 100644 core/lib/pimple/pimple/README.rst delete mode 100644 core/lib/pimple/pimple/composer.json delete mode 100644 core/lib/pimple/pimple/lib/Pimple.php delete mode 100644 core/lib/pimple/pimple/phpunit.xml.dist delete mode 100644 core/lib/scan/kss-php/.editorconfig delete mode 100644 core/lib/scan/kss-php/.gitignore delete mode 100644 core/lib/scan/kss-php/ACKNOWLEDGEMENTS delete mode 100644 core/lib/scan/kss-php/CONTRIBUTING.md delete mode 100644 core/lib/scan/kss-php/LICENSE delete mode 100644 core/lib/scan/kss-php/README.md delete mode 100644 core/lib/scan/kss-php/build.xml delete mode 100644 core/lib/scan/kss-php/composer.json delete mode 100644 core/lib/scan/kss-php/composer.lock delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Exception/UnexpectedValueException.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Modifier.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Parser.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/Kss/Section.php delete mode 100644 core/lib/scan/kss-php/lib/Scan/kss.coffee delete mode 100644 core/lib/scan/kss-php/phpunit.xml.dist delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json delete mode 100644 core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json delete mode 100644 core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/.gitignore delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Finder.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Glob.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/LICENSE delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/README.md delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/composer.json delete mode 100644 core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/.gitignore delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/LICENSE delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Process.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/README.md delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/Tests/SimpleProcessTest.php delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/composer.json delete mode 100644 core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/README.md delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json delete mode 100644 core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist delete mode 100644 core/migrations/001-move-source.json delete mode 100644 core/migrations/002-check-public-styleguide-exists.json delete mode 100644 core/migrations/003-move-styleguide.json delete mode 100644 core/migrations/004-check-meta-exists.json delete mode 100644 core/migrations/005-move-pattern-header-footer.json delete mode 100644 core/migrations/006-check-public-data-exists.json delete mode 100644 core/migrations/007-check-public-patterns-exists.json delete mode 100755 core/pageFollowServer.php delete mode 100644 core/scripts/README delete mode 100755 core/scripts/generateSite.command delete mode 100755 core/scripts/generateSiteWithCSS.command delete mode 100755 core/scripts/startAutoReloadServer.command delete mode 100755 core/scripts/startPageFollowServer.command delete mode 100755 core/scripts/startWatcher.command delete mode 100755 core/scripts/startWatcherWithAutoReload.command delete mode 100644 core/source/_annotations/annotations.js delete mode 100644 core/source/_data/data.json delete mode 100644 core/source/_data/listitems.json delete mode 100644 core/source/_meta/_00-head.mustache delete mode 100644 core/source/_meta/_01-foot.mustache delete mode 100644 core/source/_patterns/00-atoms/01-global/00-colors.mustache delete mode 100644 core/source/_patterns/00-atoms/01-global/01-fonts.mustache delete mode 100644 core/source/_patterns/00-atoms/01-global/02-animations.mustache delete mode 100644 core/source/_patterns/00-atoms/01-global/03-visibility.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/00-headings.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/01-paragraph.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/02-blockquote.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/03-inline-elements.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/04-time.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/05-preformatted-text.mustache delete mode 100644 core/source/_patterns/00-atoms/02-text/06-hr.mustache delete mode 100644 core/source/_patterns/00-atoms/03-lists/00-unordered.mustache delete mode 100644 core/source/_patterns/00-atoms/03-lists/01-ordered.mustache delete mode 100644 core/source/_patterns/00-atoms/03-lists/02-definition.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/00-logo.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/01-landscape-4x3.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/02-landscape-16x9.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/03-square.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/04-avatar.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/05-icons.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/06-loading-icon.mustache delete mode 100644 core/source/_patterns/00-atoms/04-images/07-favicon.mustache delete mode 100644 core/source/_patterns/00-atoms/05-forms/00-text-fields.mustache delete mode 100644 core/source/_patterns/00-atoms/05-forms/01-select-menu.mustache delete mode 100644 core/source/_patterns/00-atoms/05-forms/02-checkbox.mustache delete mode 100644 core/source/_patterns/00-atoms/05-forms/03-radio-buttons.mustache delete mode 100644 core/source/_patterns/00-atoms/05-forms/04-html5-inputs.mustache delete mode 100644 core/source/_patterns/00-atoms/06-buttons/00-buttons.mustache delete mode 100644 core/source/_patterns/00-atoms/07-tables/00-table.mustache delete mode 100644 core/source/_patterns/00-atoms/08-media/_00-video.mustache delete mode 100644 core/source/_patterns/00-atoms/08-media/_01-audio.mustache delete mode 100644 core/source/_patterns/01-molecules/00-text/00-byline.mustache delete mode 100644 core/source/_patterns/01-molecules/00-text/01-address.mustache delete mode 100644 core/source/_patterns/01-molecules/00-text/02-heading-group.mustache delete mode 100644 core/source/_patterns/01-molecules/00-text/03-blockquote-with-citation.mustache delete mode 100644 core/source/_patterns/01-molecules/00-text/04-intro-text.mustache delete mode 100644 core/source/_patterns/01-molecules/01-layout/00-one-up.mustache delete mode 100644 core/source/_patterns/01-molecules/01-layout/01-two-up.mustache delete mode 100644 core/source/_patterns/01-molecules/01-layout/02-three-up.mustache delete mode 100644 core/source/_patterns/01-molecules/01-layout/03-four-up.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/00-media-block.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/01-block-headline-byline.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/02-block-hero.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/03-block-thumb-headline.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/04-block-headline.mustache delete mode 100644 core/source/_patterns/01-molecules/02-blocks/05-block-inset.mustache delete mode 100644 core/source/_patterns/01-molecules/03-media/00-figure-with-caption.mustache delete mode 100644 core/source/_patterns/01-molecules/04-forms/00-search.mustache delete mode 100644 core/source/_patterns/01-molecules/04-forms/01-comment-form.mustache delete mode 100644 core/source/_patterns/01-molecules/04-forms/02-newsletter.mustache delete mode 100644 core/source/_patterns/01-molecules/05-navigation/00-primary-nav.mustache delete mode 100644 core/source/_patterns/01-molecules/05-navigation/01-footer-nav.mustache delete mode 100644 core/source/_patterns/01-molecules/05-navigation/02-breadcrumbs.mustache delete mode 100644 core/source/_patterns/01-molecules/05-navigation/03-pagination.mustache delete mode 100644 core/source/_patterns/01-molecules/05-navigation/04-tabs.mustache delete mode 100644 core/source/_patterns/01-molecules/06-components/00-social-share.mustache delete mode 100644 core/source/_patterns/01-molecules/06-components/01-accordion.mustache delete mode 100644 core/source/_patterns/01-molecules/06-components/02-single-comment.mustache delete mode 100644 core/source/_patterns/01-molecules/07-messaging/00-alert.mustache delete mode 100644 core/source/_patterns/02-organisms/00-global/00-header.mustache delete mode 100644 core/source/_patterns/02-organisms/00-global/01-footer.mustache delete mode 100644 core/source/_patterns/02-organisms/01-article/00-article-body.mustache delete mode 100644 core/source/_patterns/02-organisms/02-comments/00-comment-thread.mustache delete mode 100644 core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache delete mode 100644 core/source/_patterns/02-organisms/03-sections/01-recent-tweets.mustache delete mode 100644 core/source/_patterns/02-organisms/03-sections/02-related-posts.mustache delete mode 100644 core/source/_patterns/03-templates/00-homepage.mustache delete mode 100644 core/source/_patterns/03-templates/01-blog.mustache delete mode 100644 core/source/_patterns/03-templates/02-article.mustache delete mode 100644 core/source/_patterns/04-pages/00-homepage.json delete mode 100644 core/source/_patterns/04-pages/00-homepage.mustache delete mode 100644 core/source/_patterns/04-pages/00-homepage~emergency.json delete mode 100644 core/source/_patterns/04-pages/01-blog.json delete mode 100644 core/source/_patterns/04-pages/01-blog.mustache delete mode 100644 core/source/_patterns/04-pages/02-article.json delete mode 100644 core/source/_patterns/04-pages/02-article.mustache delete mode 100644 core/source/css/scss/base/_animation.scss delete mode 100644 core/source/css/scss/base/_forms.scss delete mode 100644 core/source/css/scss/base/_global-classes.scss delete mode 100644 core/source/css/scss/base/_headings.scss delete mode 100644 core/source/css/scss/base/_links.scss delete mode 100644 core/source/css/scss/base/_lists.scss delete mode 100644 core/source/css/scss/base/_main.scss delete mode 100644 core/source/css/scss/base/_media.scss delete mode 100644 core/source/css/scss/base/_tables.scss delete mode 100644 core/source/css/scss/base/_text.scss delete mode 100644 core/source/css/scss/generic/_mixins.scss delete mode 100644 core/source/css/scss/generic/_reset.scss delete mode 100644 core/source/css/scss/generic/_variables.scss delete mode 100644 core/source/css/scss/objects/_accordion.scss delete mode 100644 core/source/css/scss/objects/_article.scss delete mode 100644 core/source/css/scss/objects/_blocks.scss delete mode 100644 core/source/css/scss/objects/_buttons.scss delete mode 100644 core/source/css/scss/objects/_carousels.scss delete mode 100644 core/source/css/scss/objects/_comments.scss delete mode 100644 core/source/css/scss/objects/_footer.scss delete mode 100644 core/source/css/scss/objects/_header.scss delete mode 100644 core/source/css/scss/objects/_icons.scss delete mode 100644 core/source/css/scss/objects/_layout.scss delete mode 100644 core/source/css/scss/objects/_lists.scss delete mode 100644 core/source/css/scss/objects/_main.scss delete mode 100644 core/source/css/scss/objects/_messaging.scss delete mode 100644 core/source/css/scss/objects/_nav.scss delete mode 100644 core/source/css/scss/objects/_sections.scss delete mode 100644 core/source/css/scss/objects/_tabs.scss delete mode 100644 core/source/css/scss/objects/_text.scss delete mode 100644 core/source/css/scss/objects/_tooltip.scss delete mode 100644 core/source/css/style.css delete mode 100644 core/source/css/style.scss delete mode 100644 core/source/favicon.ico delete mode 100644 core/source/fonts/icons.dev.svg delete mode 100644 core/source/fonts/icons.eot delete mode 100644 core/source/fonts/icons.svg delete mode 100644 core/source/fonts/icons.ttf delete mode 100644 core/source/fonts/icons.woff delete mode 100644 core/source/images/ajax-loader.gif delete mode 100644 core/source/images/favicon_16x16.jpg delete mode 100644 core/source/images/favicon_32x32.jpg delete mode 100644 core/source/images/fpo_16x9.png delete mode 100644 core/source/images/fpo_4x3.png delete mode 100644 core/source/images/fpo_avatar.png delete mode 100644 core/source/images/fpo_square.png delete mode 100644 core/source/images/logo.png delete mode 100644 core/source/images/sample/landscape-16x9-mountains.jpg delete mode 100644 core/source/images/sample/thumb-square-fire.jpg delete mode 100644 core/source/images/sample/thumb-square-gear.jpg delete mode 100644 core/source/images/sample/thumb-square-ivy.jpg delete mode 100644 core/source/images/sample/thumb-square-river.jpg delete mode 100644 core/source/images/sample/thumb-square-yosemite.jpg delete mode 100644 core/source/images/sample/tout-4x3-climber.jpg delete mode 100644 core/source/images/sample/tout-4x3-climbers.jpg delete mode 100644 core/source/images/sample/tout-4x3-stream.jpg delete mode 100644 core/source/js/fitvids.js delete mode 100644 core/source/js/init.js delete mode 100644 core/source/js/jquery-2.0.0b2.js delete mode 100644 core/source/js/modernizr.js delete mode 100644 core/styleguide/css/styleguide-specific.css delete mode 100644 core/styleguide/css/styleguide-specific.scss delete mode 100755 core/styleguide/css/styleguide.css delete mode 100755 core/styleguide/css/styleguide.scss delete mode 100644 core/styleguide/css/vendor/prism.css delete mode 100644 core/styleguide/css/vendor/typeahead.css delete mode 100755 core/styleguide/fonts/icomoon.eot delete mode 100755 core/styleguide/fonts/icomoon.svg delete mode 100755 core/styleguide/fonts/icomoon.ttf delete mode 100755 core/styleguide/fonts/icomoon.woff delete mode 100644 core/styleguide/html/README delete mode 100644 core/styleguide/images/spinner.gif delete mode 100644 core/styleguide/js/annotations-pattern.js delete mode 100644 core/styleguide/js/annotations-viewer.js delete mode 100644 core/styleguide/js/code-pattern.js delete mode 100644 core/styleguide/js/code-viewer.js delete mode 100644 core/styleguide/js/data-saver.js delete mode 100644 core/styleguide/js/pattern-finder.js delete mode 100644 core/styleguide/js/postmessage.js delete mode 100644 core/styleguide/js/qrcode-generator.js delete mode 100644 core/styleguide/js/styleguide.js delete mode 100644 core/styleguide/js/synclisteners.js delete mode 100644 core/styleguide/js/url-handler.js delete mode 100644 core/styleguide/js/vendor/classlist-polyfill.js delete mode 100644 core/styleguide/js/vendor/jquery.js delete mode 100644 core/styleguide/js/vendor/jwerty.js delete mode 100644 core/styleguide/js/vendor/prism.js delete mode 100644 core/styleguide/js/vendor/typeahead.bundle.min.js delete mode 100644 core/templates/README delete mode 100644 core/templates/index.mustache delete mode 100644 core/templates/partials/ipAddress.mustache delete mode 100644 core/templates/partials/ishControls.mustache delete mode 100644 core/templates/partials/patternNav.mustache delete mode 100644 core/templates/partials/patternPaths.mustache delete mode 100644 core/templates/partials/patternSection.mustache delete mode 100644 core/templates/partials/patternSectionSubtype.mustache delete mode 100644 core/templates/partials/viewAllPaths.mustache delete mode 100644 core/templates/partials/viewerAnnotation.mustache delete mode 100644 core/templates/partials/viewerCode.mustache delete mode 100644 core/templates/partials/websockets.mustache delete mode 100644 core/templates/pattern-header-footer/README delete mode 100644 core/templates/pattern-header-footer/footer-pattern.html delete mode 100644 core/templates/pattern-header-footer/footer.html delete mode 100644 core/templates/pattern-header-footer/header.html delete mode 100644 core/templates/snapshot.mustache delete mode 100644 core/templates/viewall.mustache delete mode 100644 extras/apache/README delete mode 100644 extras/apache/vhost.txt diff --git a/composer.json b/composer.json deleted file mode 100644 index 75f5b3bdf..000000000 --- a/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "config": { - "vendor-dir": "core/lib" - }, - "autoload": { - "psr-0": { - "": "core/lib/" - } - }, - "require": { - "alchemy/zippy": "0.2.0", - "michelf/php-markdown": "1.4", - "symfony/yaml": "2.4.4", - "scan/kss-php": "v0.6.0" - } -} diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 8e1cf3147..000000000 --- a/composer.lock +++ /dev/null @@ -1,644 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" - ], - "hash": "3d9707b037b50d1292b7def1040f4095", - "packages": [ - { - "name": "alchemy/zippy", - "version": "0.2.0", - "source": { - "type": "git", - "url": "https://github.com/alchemy-fr/Zippy.git", - "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/alchemy-fr/Zippy/zipball/e652161e1d99f647b34c3ff9695bb4bb0b8837cf", - "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf", - "shasum": "" - }, - "require": { - "doctrine/collections": "~1.0", - "guzzle/guzzle": "~3.0", - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/filesystem": "~2.0", - "symfony/process": "~2.0" - }, - "require-dev": { - "ext-zip": "*", - "phpunit/phpunit": "~3.7", - "sami/sami": "dev-master@dev", - "symfony/finder": "~2.0" - }, - "suggest": { - "ext-zip": "To use the ZipExtensionAdapter" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.2.x-dev" - } - }, - "autoload": { - "psr-0": { - "Alchemy": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alchemy", - "email": "dev.team@alchemy.fr", - "homepage": "http://www.alchemy.fr/" - } - ], - "description": "Zippy, the archive manager companion", - "keywords": [ - "bzip", - "compression", - "tar", - "zip" - ], - "time": "2014-04-04 16:24:46" - }, - { - "name": "doctrine/collections", - "version": "v1.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", - "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/", - "role": "Creator" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "http://jmsyst.com", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2014-02-03 23:07:43" - }, - { - "name": "guzzle/guzzle", - "version": "v3.9.1", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle3.git", - "reference": "92d9934f2fca1da15178c91239576ae26e505e60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60", - "reference": "92d9934f2fca1da15178c91239576ae26e505e60", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.3", - "symfony/event-dispatcher": "~2.1" - }, - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - "require-dev": { - "doctrine/cache": "~1.3", - "monolog/monolog": "~1.0", - "phpunit/phpunit": "3.7.*", - "psr/log": "~1.0", - "symfony/class-loader": "~2.1", - "zendframework/zend-cache": "2.*,<2.3", - "zendframework/zend-log": "2.*,<2.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.8-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle": "src/", - "Guzzle\\Tests": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2014-05-07 17:04:22" - }, - { - "name": "michelf/php-markdown", - "version": "1.4.0", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-markdown.git", - "reference": "96d8150406f67e683ef4acc09fef91785fef1266" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-markdown/zipball/96d8150406f67e683ef4acc09fef91785fef1266", - "reference": "96d8150406f67e683ef4acc09fef91785fef1266", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-lib": "1.4.x-dev" - } - }, - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "http://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "http://daringfireball.net/" - } - ], - "description": "PHP Markdown", - "homepage": "http://michelf.ca/projects/php-markdown/", - "keywords": [ - "markdown" - ], - "time": "2013-11-29 17:09:24" - }, - { - "name": "pimple/pimple", - "version": "v1.1.1", - "source": { - "type": "git", - "url": "https://github.com/fabpot/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ], - "time": "2013-11-22 08:30:29" - }, - { - "name": "scan/kss-php", - "version": "v0.6.0", - "source": { - "type": "git", - "url": "https://github.com/scaninc/kss-php.git", - "reference": "5acddff2758761252d8e6da97680ef1f412457cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/scaninc/kss-php/zipball/5acddff2758761252d8e6da97680ef1f412457cb", - "reference": "5acddff2758761252d8e6da97680ef1f412457cb", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/finder": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*" - }, - "type": "library", - "autoload": { - "psr-0": { - "Scan\\Kss": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Russell Ahlstrom", - "email": "russell.ahlstrom@gmail.com", - "homepage": "http://russell.ahlstromology.com" - } - ], - "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", - "keywords": [ - "css documentation", - "kss", - "styleguide" - ], - "time": "2013-11-11 17:49:11" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.4.4", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "e539602e5455aa086c0e81e604745af7789e4d8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a", - "reference": "e539602e5455aa086c0e81e604745af7789e4d8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~2.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com", - "time": "2014-04-16 10:34:31" - }, - { - "name": "symfony/filesystem", - "version": "v2.4.4", - "target-dir": "Symfony/Component/Filesystem", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4", - "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2014-04-16 10:34:31" - }, - { - "name": "symfony/finder", - "version": "v2.4.4", - "target-dir": "Symfony/Component/Finder", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/25e1e7d5e7376f8a92ae3b1d714d956edf33a730", - "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "http://symfony.com", - "time": "2014-04-27 13:34:57" - }, - { - "name": "symfony/process", - "version": "v2.4.4", - "target-dir": "Symfony/Component/Process", - "source": { - "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", - "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Process\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "http://symfony.com", - "time": "2014-04-27 13:34:57" - }, - { - "name": "symfony/yaml", - "version": "v2.4.4", - "target-dir": "Symfony/Component/Yaml", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/65539ecde838f9c0d18b006b2101e3deb4b5c9ff", - "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2014-04-18 20:37:09" - } - ], - "packages-dev": [ - - ], - "aliases": [ - - ], - "minimum-stability": "stable", - "stability-flags": [ - - ], - "platform": [ - - ], - "platform-dev": [ - - ] -} diff --git a/core/autoReloadServer.php b/core/autoReloadServer.php deleted file mode 100755 index 9c3ede53b..000000000 --- a/core/autoReloadServer.php +++ /dev/null @@ -1,45 +0,0 @@ -register(); - -// parse the main config for the content sync port -if (!($config = @parse_ini_file(__DIR__."/../config/config.ini"))) { - print "Missing the configuration file. Please build it using the Pattern Lab builder.\n"; - exit; -} - -// give it a default port -$port = ($config) ? trim($config['autoReloadPort']) : '8001'; -$args = getopt("s"); -$newlines = (isset($args["s"])) ? true : false; - -// start the content sync server -$server = new \Wrench\Server('ws://0.0.0.0:'.$port.'/', array()); - -// register the application -$server->registerApplication('autoreload', new \Wrench\Application\AutoReloadApplication($newlines)); - -if (!isset($args["s"])) { - print "\n"; - print "Auto-reload Server Started...\n"; - print "Use CTRL+C to stop this service...\n"; -} - -// run it -$server->run(); diff --git a/core/builder.php b/core/builder.php deleted file mode 100644 index 26e4204dc..000000000 --- a/core/builder.php +++ /dev/null @@ -1,135 +0,0 @@ -setCommand("g","generate","Generate Pattern Lab","The generate command generates an entire site a single time. By default it removes old content in public/, compiles the patterns and moves content from source/ into public/"); -$console->setCommandOption("g","p","patternsonly","Generate only the patterns. Does NOT clean public/.","To generate only the patterns:"); -$console->setCommandOption("g","n","nocache","Set the cacheBuster value to 0.","To turn off the cacheBuster:"); -$console->setCommandOption("g","c","enablecss","Generate CSS for each pattern. Resource intensive.","To run and generate the CSS for each pattern:"); - -// set-up an alias for the generate command -$console->setCommand("b","build","Alias for the generate command","Alias for the generate command. Please refer to it's help for full options."); - -// set-up the watch command and options -$console->setCommand("w","watch","Watch for changes and regenerate","The watch command builds Pattern Lab, watches for changes in source/ and regenerates Pattern Lab when there are any."); -$console->setCommandOption("w","p","patternsonly","Watches only the patterns. Does NOT clean public/.","To watch and generate only the patterns:"); -$console->setCommandOption("w","n","nocache","Set the cacheBuster value to 0.","To turn off the cacheBuster:"); -$console->setCommandOption("w","r","autoreload","Turn on the auto-reload service.","To turn on auto-reload:"); - -// set-up the snapshot command and options -$console->setCommand("s","snapshot","Take a snapshot of public/","The snapshot command copies the current state of public/ and puts it in snapshots/v*/."); -$console->setCommandOption("s","d:","dir:","Optional directory path","To add an optional directory path instead of the defaul v*/ path:","example-path/"); - -// set-up the fetch command and options -$console->setCommand("f:","fetch:","Fetch a starter kit","The fetch command grabs a starter kit from GitHub and puts it into source/."); -$console->setCommandSample("f","Install a starter kit:","github-org/github-repo"); -$console->setCommandSample("f","Install a tagged version of a starter kit:","github-org/github-repo#tag"); - -// set-up the version command -$console->setCommand("v","version","Print the version number","The version command prints out the current version of Pattern Lab."); - -// set-up the help command -$console->setCommand("h:","help:","Print the help for a given command","The help command prints out the help for a given flag. Just use -h with another command and it will tell you all of the options."); - -/******************************* - * Figure out what to run - *******************************/ - -// get what was passed on the command line -$console->getArguments(); - -if ($console->findCommand("h|help")) { - - $helpCommand = $console->findCommandValue("h|help"); - $helpCommand = str_replace("-","",$helpCommand); - $helpCommand = (strlen($helpCommand) == 1) ? $helpCommand : $console->findCommandShort($helpCommand); - - $helpCommand ? $console->writeHelpCommand($helpCommand) : $console->writeHelp(); - -} else if ($command = $console->getCommand()) { - - // run commands - - // load Pattern Lab's config, if first time set-up move files appropriately too - PatternLab\Config::loadOptions(); - - // set-up required vars - $enableCSS = $console->findCommandOption("c|enablecss"); - $moveStatic = ($console->findCommandOption("p|patternsonly")) ? false : true; - $noCacheBuster = $console->findCommandOption("n|nocache"); - $autoReload = $console->findCommandOption("r|autoreload"); - - if (($command == "g") || ($command == "b")) { - - // load the generator - $g = new PatternLab\Generator(); - $g->generate($enableCSS,$moveStatic,$noCacheBuster); - $g->printSaying(); - - } else if ($command == "w") { - - // CSS feature should't be used with watch - $enableCSS = false; - - // load the generator - $g = new PatternLab\Generator(); - $g->generate($enableCSS,$moveStatic,$noCacheBuster); - - // load the watcher - $w = new PatternLab\Watcher(); - $w->watch($autoReload,$moveStatic,$noCacheBuster); - - } else if ($command == "s") { - - // run the snapshot command - $snapshotDir = $console->findCommandOptionValue("d|dir"); - $s = new PatternLab\Snapshot(); - $s->takeSnapshot($snapshotDir); - - } else if ($command == "f") { - - // run the snapshot command - $starterKit = $console->findCommandValue("f|fetch"); - $sk = new PatternLab\StarterKit(); - $sk->fetch($starterKit); - - } else if ($command == "v") { - - // write out the version number - print "You're running v".PatternLab\Config::$options["v"]." of the PHP version of Pattern Lab.\n"; - exit; - - } - -} else { - - // write the generic help - $console->writeHelp(); - -} diff --git a/core/config/config.ini.default b/core/config/config.ini.default deleted file mode 100644 index 58d57c38b..000000000 --- a/core/config/config.ini.default +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Configuration Options for Pattern Lab - * If config.ini doesn't exist Pattern Lab will try to create a new version - */ - -v = "0.7.12" - -// file extensions to ignore when building or watching the source dir, separate with a comma -ie = "scss,DS_Store,less" - -// directories and files to ignore when building or watching the source dir, separate with a comma -id = "scss,.svn,.sass-cache" - -// choose if these services should be loaded in the nav and their ports -autoReloadNav = "true" -autoReloadPort = "8002" -pageFollowNav = "true" -pageFollowPort = "8003" - -// whether the qr code generator should be loaded automatically in the nav -qrCodeGeneratorOn = "false" - -// pattern lab's xip host if you have it configured, to be used with the QR code generator -xipHostname = "http://patternlab.*.xip.io" - -// whether the public directory should be cleaned when generating your site -cleanPublic = "true" - -// the minimum and maximum for the viewport resizer -ishMinimum = "240" -ishMaximum = "2600" - -// which, if any, controls to hide in the nav, separate with a comma -ishControlsHide = "hay" - -// the order of pattern states, css class names -patternStates = "inprogress,inreview,complete" - -// the pattern types that shouldn't be included in the style guide, useful if you nest pages/templates -styleGuideExcludes = "" - -// should the cache buster be on, set to false to set the cacheBuster value to 0 -cacheBusterOn = "true" - -// the pattern rending engine -patternExtension = "mustache" - -// the public directory -publicDir = "public" - -// the source directory -sourceDir = "source" \ No newline at end of file diff --git a/core/lib/CSSRuleSaver/CSSRuleSaver.php b/core/lib/CSSRuleSaver/CSSRuleSaver.php deleted file mode 100644 index 412e17f4c..000000000 --- a/core/lib/CSSRuleSaver/CSSRuleSaver.php +++ /dev/null @@ -1,266 +0,0 @@ -ruleSets and $this->atRules arrays - * @param {String} the filename of the CSS file - */ - public function loadCSS($file) { - - if (!file_exists($file)) { - $this->error("The CSS file you supplied doesn't seem to exist. Check the path."); - } - - $commentOpen = false; - $atRuleOpen = false; - $declarationBlockOpen = false; - $fontFaceRuleOpen = false; - - $atRule = ""; - $declarationBlock = ""; - $selectors = ""; - - // iterate over the given file and parse it into at-rules, selectors and their given declaration blocks - $fp = fopen($file, "r"); - while(!feof($fp)) { - $current_line = fgets($fp); - if (!feof($fp)) { - - if ((strpos($current_line, "/*") !== false) && (strpos($current_line, "*/") === false)) { - - // matched a comment that *didn't* close on the same line - $commentOpen = true; - - } else if (strpos($current_line, "*/") !== false) { - - // comment closed - $commentOpen = false; - - } else if ($commentOpen) { - - // skip this line of the CSS file because we're inside a comment - - } else if (strpos($current_line, "@") !== false) { - - // matched an at-rule like a media query - $atRuleOpen = true; - $atRule = trim(str_replace("{","",$current_line)); - - // handle the weird case of the @font-face at-rule - if (strpos($current_line, "@font-face") !== false) { - $declarationBlock = ""; - $declarationBlockOpen = true; - $fontFaceRuleOpen = true; - } - - } else if (strpos($current_line, "{") !== false) { - - // matched the opening of a declaration block - $declarationBlock = ""; - $declarationBlockOpen = true; - $selectors = trim(str_replace("{","",$current_line)); - - } else if (strpos($current_line, "}") !== false) { - - // matched the closing of a declaration block or at-rule - if ($atRuleOpen && !$declarationBlockOpen) { - - // it was an at-rule. close it up - $atRuleOpen = false; - - } else { - - // it was a declaration block. close it up. - $declarationBlockOpen = false; - $declarationBlock .= "\t".trim(str_replace("}","",$current_line)); - - // if we're within an at-rule assign all the styles to it (e.g. styles under a media query) - if ($atRuleOpen) { - if (!array_key_exists($atRule,$this->atRules)) { - $this->atRules[$atRule] = array(); - } - $this->atRules[$atRule][$selectors] = !array_key_exists($selectors,$this->atRules[$atRule]) ? "\t".trim($declarationBlock) : $this->atRules[$atRule][$selectors]."\n\t".trim($declarationBlock); - } else { - $this->ruleSets[$selectors] = !array_key_exists($selectors,$this->ruleSets) ? "\t".trim($declarationBlock) : $this->ruleSets[$selectors]."\n\t".trim($declarationBlock); - } - - // wait, a font-face rule was open. close it all up - if ($fontFaceRuleOpen) { - $fontFaceRuleOpen = false; - $atRuleOpen = false; - } else if (substr_count($current_line, "}") > 1) { - - // oops, someone closed the at-rule on the same line as the declaration block - // *shakes fist at sass* - $atRuleOpen = false; - } - } - } else if ($declarationBlockOpen) { - - // declaration block is open so keep reading it in - $declarationBlock .= "\t".ltrim($current_line); - - } - } - } - fclose($fp); - - } - - /** - * Load the HTML data - * @param {String} the filename of the HTML file - */ - public function loadHTML($file,$load = true) { - if ($load) { - if (file_exists($file)) { - $this->htmlData = file_get_contents($file); - } else { - $this->error("The HTML file you supplied doesn't seem to exist. Check the path."); - } - } else { - $this->htmlData = $file; - } - } - - /** - * Save the CSS rules that match between the given CSS file and the HTML file - * - * @return {String} the rules that match between the given CSS file and HTML file - */ - public function saveRules() { - - // make sure data exists to compare - if (($this->htmlData == "") || (count($this->ruleSets) == 0)) { - $this->error("This would work better if there was CSS or HTML data."); - } - - // set-up the selector DOM to compare - $this->dom = new SelectorDOM($this->htmlData); - - // iterate over the default rule sets and test them against the given mark-up - $statements = ""; - foreach ($this->ruleSets as $selector => $declarationBlock) { - $statements .= $this->buildRuleSet($selector,$declarationBlock); - } - - // iterate over the at-rules - foreach ($this->atRules as $atRule => $ruleSets) { - - // iterate over the rule sets in the at-rules and test them against the given mark-up - $atRuleSets = ""; - foreach ($ruleSets as $selector => $declarationBlock) { - $atRuleSets .= $this->buildRuleSet($selector,$declarationBlock,"\t"); - } - - if ($atRuleSets != "") { - - // only write-out the at-rule if it contains at least one rule set - $statements .= $atRule." {\n"; - $statements .= $atRuleSets."\n"; - $statements .= "}\n\n"; - - } else if ($atRule == "@font-face") { - - // if the at-rule is a @font-face write it out no matter what - foreach ($ruleSets as $selector => $ruleSet) { - $statements .= $atRule." {\n"; - $statements .= $ruleSet."\n"; - $statements .= "}\n\n"; - } - - } - } - - unset($this->dom); - - return $statements; - - } - - /** - * Compare the given selector(s) against the DOM. Return the rule set if it matches - * @param {String} the selector(s) to test against the xPath - * @param {String} the declaration block that goes with the selector - * @param {String} any indent characters that might need to be added to the final output - * - * @return {String} if the selector(s) matched return the entire rule set with matches - */ - protected function buildRuleSet($selector,$declarationBlock,$indent = "") { - - // trap the selectors that are found - $foundSelectors = array(); - - // a given selector may have multiple parts (e.g. h1, h2, h3 ) break it up so each can be tested. - $selectors = explode(",",$selector); - - // iterate over each selector - foreach ($selectors as $selector) { - - $selector = trim($selector); - - // save the original selector and strip off bad pseudo-classes for matching purposes - $selectorOrig = $selector; - $badPseudoClasses = array(":first-child",":last-child",":after",":before",":nth-of",":visited",":hover",":focus","::"); - foreach ($badPseudoClasses as $badPseudoClass) { - $pos = strpos($selector,$badPseudoClass); - if ($pos !== false) { - $selector = substr($selector,0,$pos-strlen($selector)); - break; - } - } - - // match the selector against the DOM. if result is found save the original selector format - if (count($this->dom->select($selector)) > 0) { - $foundSelectors[] = $selectorOrig; - } - } - - // if the given selectors matched against the DOM build the rule set - if (count($foundSelectors) > 0) { - - // combine selectors that share a rule set - $i = 0; - $selectorList = ""; - foreach ($foundSelectors as $selector) { - $selectorList .= ($i > 0) ? ", ".$selector : $selector; - $i++; - } - - // write out the rule set & return it - $text = $indent.$selectorList." { \n"; - $text .= str_replace($indent,$indent.$indent,$declarationBlock)."\n"; - $text .= $indent."}\n\n"; - return $text; - - } - - } - - /** - * Print the error message. Yes, I should be using exception handling but I'm being lazy for now - * @param {String} the message to spit out - */ - protected function error($msg) { - print $msg."\n"; - exit; - } - -} diff --git a/core/lib/CSSRuleSaver/LICENSE b/core/lib/CSSRuleSaver/LICENSE deleted file mode 100644 index 2e13f8ef8..000000000 --- a/core/lib/CSSRuleSaver/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Dave Olsen, http://dmolsen.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/CSSRuleSaver/LICENSE.SelectorDOM b/core/lib/CSSRuleSaver/LICENSE.SelectorDOM deleted file mode 100644 index 55c4aabe3..000000000 --- a/core/lib/CSSRuleSaver/LICENSE.SelectorDOM +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright (c) 2008 - 2009 TJ Holowaychuk - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/core/lib/CSSRuleSaver/SelectorDOM.php b/core/lib/CSSRuleSaver/SelectorDOM.php deleted file mode 100755 index 342148559..000000000 --- a/core/lib/CSSRuleSaver/SelectorDOM.php +++ /dev/null @@ -1,178 +0,0 @@ - MIT Licensed - * - * Persitant object for selecting elements. - * - * $dom = new SelectorDOM($html); - * $links = $dom->select('a'); - * $list_links = $dom->select('ul li a'); - * - */ -class SelectorDOM { - - const VERSION = '1.1.3'; - - /** - * @var DOMXPath - */ - protected $xpath; - - /** - * Map of regexes to convert CSS selector to XPath - * - * @var array - */ - public static $regexMap = array( - '/\s*,\s*/' => '|descendant-or-self::', - '/:(button|submit|file|checkbox|radio|image|reset|text|password)/' => 'input[@type="\1"]', - '/\[(\w+)\]/' => '*[@\1]', # [id] - '/\[(\w+)=[\'"]?(.*?)[\'"]?\]/' => '[@\1="\2"]', # foo[id=foo] - '/^\[/' => '*[', # [id=foo] - '/([\w\-]+)\#([\w\-]+)/' => '\1[@id="\2"]', # div#foo - '/\#([\w\-]+)/' => '*[@id="\1"]', # #foo - '/([\w\-]+)\.([\w\-]+)/' => '\1[contains(concat(" ",@class," ")," \2 ")]', # div.foo - '/\.([\w\-]+)/' => '*[contains(concat(" ",@class," ")," \1 ")]', # .foo - '/([\w\-]+):first-child/' => '*/\1[position()=1]', - '/([\w\-]+):last-child/' => '*/\1[position()=last()]', - '/:first-child/' => '*/*[position()=1]', - '/:last-child/' => '*/*[position()=last()]', - '/([\w\-]+):nth-child\((\d+)\)/' => '*/\1[position()=\2]', - '/:nth-child\((\d+)\)/' => '*/*[position()=\1]', - '/([\w\-]+):contains\((.*?)\)/' => '\1[contains(string(.),"\2")]', - '/\s*>\s*/' => '/', # > - '/\s*~\s*/' => '/following-sibling::', # ~ - '/\s*\+\s*([\w\-]+)/' => '/following-sibling::\1[position()=1]', # + - '/\]\*/' => ']', - '/\]\/\*/' => ']', - ); - - /** - * Load $data into the object - * - * @param string|DOMDocument $data - * @param array $errors A by-ref capture for libxml error messages. - */ - public function __construct($data, &$errors = null) { - # Wrap this with libxml errors off - # this both sets the new value, and returns the previous. - $lib_xml_errors = libxml_use_internal_errors(true); - - if (is_a($data, 'DOMDocument')) { - $this->xpath = new \DOMXpath($data); - } else { - $dom = new \DOMDocument(); - $dom->loadHTML($data); - $this->xpath = new \DOMXpath($dom); - } - - # Clear any errors and restore the original value - $errors = libxml_get_errors(); - libxml_clear_errors(); - libxml_use_internal_errors($lib_xml_errors); - } - - /** - * Select elements from the loaded HTML using the css $selector. - * When $as_array is true elements and their children will - * be converted to array's containing the following keys (defaults to true): - * - * - name : element name - * - text : element text - * - children : array of children elements - * - attributes : attributes array - * - * Otherwise regular DOMElement's will be returned. - * - * @param string $selector CSS Selector - * @param boolean $as_array Whether to return an array or DOMElement - */ - public function select($selector, $as_array = true) { - $elements = $this->xpath->evaluate(self::selectorToXpath($selector)); - return $as_array ? self::elementsToArray($elements) : $elements; - } - - /** - * This allows a static access to the class, in the same way as the - * `select_elements` function did. - * - * @see $this->select() - * @param string $html - * @param string $selector CSS Selector - */ - public static function selectElements($selector, $html, $as_array = true) { - $dom = new SelectorDOM($html); - return $dom->select($selector, $as_array); - } - - /** - * Convert $elements to an array. - * - * @param DOMNodeList $elements - */ - public function elementsToArray($elements) { - $array = array(); - for ($i = 0, $length = $elements->length; $i < $length; ++$i) { - if ($elements->item($i)->nodeType == XML_ELEMENT_NODE) { - array_push($array, self::elementToArray($elements->item($i))); - } - } - return $array; - } - - /** - * Convert $element to an array. - */ - public function elementToArray($element) { - $array = array( - 'name' => $element->nodeName, - 'attributes' => array(), - 'text' => $element->textContent, - 'children' => self::elementsToArray($element->childNodes), - ); - if ($element->attributes->length) { - foreach($element->attributes as $key => $attr) { - $array['attributes'][$key] = $attr->value; - } - } - return $array; - } - - /** - * Convert $selector into an XPath string. - */ - public static function selectorToXpath($selector) { - // remove spaces around operators - $selector = preg_replace('/\s*(>|~|\+|,)\s*/', '$1', $selector); - $selectors = preg_split("/\s+/", $selector); - // Process all regular expressions to convert selector to XPath - foreach ($selectors as &$selector) { - foreach (self::$regexMap as $regex => $replacement) { - $selector = preg_replace($regex, $replacement, $selector); - } - } - $selector = implode('/descendant::', $selectors); - $selector = 'descendant-or-self::' . $selector; - return $selector; - } - -} - -# -# Procedural components -# - -define('SELECTOR_VERSION', SelectorDOM::VERSION); - -/** - * Provides a procedural function to select use SelectorDOM::select() - * on some HTML. - */ -function select_elements($selector, $html, $as_array = true) { - return SelectorDOM::selectElements($selector, $html, $as_array); -} - diff --git a/core/lib/Mustache/Autoloader.php b/core/lib/Mustache/Autoloader.php deleted file mode 100644 index df48536d0..000000000 --- a/core/lib/Mustache/Autoloader.php +++ /dev/null @@ -1,69 +0,0 @@ -baseDir = dirname(__FILE__).'/..'; - } else { - $this->baseDir = rtrim($baseDir, '/'); - } - } - - /** - * Register a new instance as an SPL autoloader. - * - * @param string $baseDir Mustache library base directory (default: dirname(__FILE__).'/..') - * - * @return Mustache_Autoloader Registered Autoloader instance - */ - public static function register($baseDir = null) - { - $loader = new self($baseDir); - spl_autoload_register(array($loader, 'autoload')); - - return $loader; - } - - /** - * Autoload Mustache classes. - * - * @param string $class - */ - public function autoload($class) - { - if ($class[0] === '\\') { - $class = substr($class, 1); - } - - if (strpos($class, 'Mustache') !== 0) { - return; - } - - $file = sprintf('%s/%s.php', $this->baseDir, str_replace('_', '/', $class)); - if (is_file($file)) { - require $file; - } - } -} diff --git a/core/lib/Mustache/Compiler.php b/core/lib/Mustache/Compiler.php deleted file mode 100644 index a95c9194b..000000000 --- a/core/lib/Mustache/Compiler.php +++ /dev/null @@ -1,475 +0,0 @@ -pragmas = array(); - $this->sections = array(); - $this->source = $source; - $this->indentNextLine = true; - $this->customEscape = $customEscape; - $this->charset = $charset; - $this->strictCallables = $strictCallables; - - return $this->writeCode($tree, $name); - } - - /** - * Helper function for walking the Mustache token parse tree. - * - * @throws Mustache_Exception_SyntaxException upon encountering unknown token types. - * - * @param array $tree Parse tree of Mustache tokens - * @param int $level (default: 0) - * - * @return string Generated PHP source code - */ - private function walk(array $tree, $level = 0) - { - $code = ''; - $level++; - foreach ($tree as $node) { - switch ($node[Mustache_Tokenizer::TYPE]) { - case Mustache_Tokenizer::T_PRAGMA: - $this->pragmas[$node[Mustache_Tokenizer::NAME]] = true; - break; - - case Mustache_Tokenizer::T_SECTION: - $code .= $this->section( - $node[Mustache_Tokenizer::NODES], - $node[Mustache_Tokenizer::NAME], - $node[Mustache_Tokenizer::INDEX], - $node[Mustache_Tokenizer::END], - $node[Mustache_Tokenizer::OTAG], - $node[Mustache_Tokenizer::CTAG], - $level - ); - break; - - case Mustache_Tokenizer::T_INVERTED: - $code .= $this->invertedSection( - $node[Mustache_Tokenizer::NODES], - $node[Mustache_Tokenizer::NAME], - $level - ); - break; - - case Mustache_Tokenizer::T_PARTIAL: - case Mustache_Tokenizer::T_PARTIAL_2: - $code .= $this->partial( - $node[Mustache_Tokenizer::NAME], - isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '', - $level - ); - break; - - case Mustache_Tokenizer::T_UNESCAPED: - case Mustache_Tokenizer::T_UNESCAPED_2: - $code .= $this->variable($node[Mustache_Tokenizer::NAME], false, $level); - break; - - case Mustache_Tokenizer::T_COMMENT: - break; - - case Mustache_Tokenizer::T_ESCAPED: - $code .= $this->variable($node[Mustache_Tokenizer::NAME], true, $level); - break; - - case Mustache_Tokenizer::T_TEXT: - $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level); - break; - - default: - throw new Mustache_Exception_SyntaxException(sprintf('Unknown token type: %s', $node[Mustache_Tokenizer::TYPE]), $node); - } - } - - return $code; - } - - const KLASS = 'lambdaHelper = new Mustache_LambdaHelper($this->mustache, $context); - $buffer = \'\'; - %s - - return $buffer; - } - %s - }'; - - const KLASS_NO_LAMBDAS = 'walk($tree); - $sections = implode("\n", $this->sections); - $klass = empty($this->sections) ? self::KLASS_NO_LAMBDAS : self::KLASS; - $callable = $this->strictCallables ? $this->prepare(self::STRICT_CALLABLE) : ''; - - return sprintf($this->prepare($klass, 0, false, true), $name, $callable, $code, $sections); - } - - const SECTION_CALL = ' - // %s section - $buffer .= $this->section%s($context, $indent, $context->%s(%s)); - '; - - const SECTION = ' - private function section%s(Mustache_Context $context, $indent, $value) - { - $buffer = \'\'; - if (%s) { - $source = %s; - $buffer .= $this->mustache - ->loadLambda((string) call_user_func($value, $source, $this->lambdaHelper)%s) - ->renderInternal($context, $indent); - } elseif (!empty($value)) { - $values = $this->isIterable($value) ? $value : array($value); - foreach ($values as $value) { - $context->push($value);%s - $context->pop(); - } - } - - return $buffer; - }'; - - /** - * Generate Mustache Template section PHP source. - * - * @param array $nodes Array of child tokens - * @param string $id Section name - * @param int $start Section start offset - * @param int $end Section end offset - * @param string $otag Current Mustache opening tag - * @param string $ctag Current Mustache closing tag - * @param int $level - * - * @return string Generated section PHP source code - */ - private function section($nodes, $id, $start, $end, $otag, $ctag, $level) - { - $method = $this->getFindMethod($id); - $id = var_export($id, true); - $source = var_export(substr($this->source, $start, $end - $start), true); - $callable = $this->getCallable(); - - if ($otag !== '{{' || $ctag !== '}}') { - $delims = ', '.var_export(sprintf('{{= %s %s =}}', $otag, $ctag), true); - } else { - $delims = ''; - } - - $key = ucfirst(md5($delims."\n".$source)); - - if (!isset($this->sections[$key])) { - $this->sections[$key] = sprintf($this->prepare(self::SECTION), $key, $callable, $source, $delims, $this->walk($nodes, 2)); - } - - return sprintf($this->prepare(self::SECTION_CALL, $level), $id, $key, $method, $id); - } - - const INVERTED_SECTION = ' - // %s inverted section - $value = $context->%s(%s); - if (empty($value)) { - %s - }'; - - /** - * Generate Mustache Template inverted section PHP source. - * - * @param array $nodes Array of child tokens - * @param string $id Section name - * @param int $level - * - * @return string Generated inverted section PHP source code - */ - private function invertedSection($nodes, $id, $level) - { - $method = $this->getFindMethod($id); - $id = var_export($id, true); - - return sprintf($this->prepare(self::INVERTED_SECTION, $level), $id, $method, $id, $this->walk($nodes, $level)); - } - - const PARTIAL = ' - if ($partial = $this->mustache->loadPartial(%s)) { - $buffer .= $partial->renderInternal($context, %s); - } - '; - - /** - * Generate Mustache Template partial call PHP source. - * - * @param string $id Partial name - * @param string $indent Whitespace indent to apply to partial - * @param int $level - * - * @return string Generated partial call PHP source code - */ - private function partial($id, $indent, $level) - { - return sprintf( - $this->prepare(self::PARTIAL, $level), - var_export($id, true), - var_export($indent, true) - ); - } - - const VARIABLE = ' - $value = $this->resolveValue($context->%s(%s), $context, $indent);%s - $buffer .= %s%s; - '; - - /** - * Generate Mustache Template variable interpolation PHP source. - * - * @param string $id Variable name - * @param boolean $escape Escape the variable value for output? - * @param int $level - * - * @return string Generated variable interpolation PHP source - */ - private function variable($id, $escape, $level) - { - $filters = ''; - - if (isset($this->pragmas[Mustache_Engine::PRAGMA_FILTERS])) { - list($id, $filters) = $this->getFilters($id, $level); - } - - $method = $this->getFindMethod($id); - $id = ($method !== 'last') ? var_export($id, true) : ''; - $value = $escape ? $this->getEscape() : '$value'; - - return sprintf($this->prepare(self::VARIABLE, $level), $method, $id, $filters, $this->flushIndent(), $value); - } - - /** - * Generate Mustache Template variable filtering PHP source. - * - * @param string $id Variable name - * @param int $level - * - * @return string Generated variable filtering PHP source - */ - private function getFilters($id, $level) - { - $filters = array_map('trim', explode('|', $id)); - $id = array_shift($filters); - - return array($id, $this->getFilter($filters, $level)); - } - - const FILTER = ' - $filter = $context->%s(%s); - if (!(%s)) { - throw new Mustache_Exception_UnknownFilterException(%s); - } - $value = call_user_func($filter, $value);%s - '; - - /** - * Generate PHP source for a single filter. - * - * @param array $filters - * @param int $level - * - * @return string Generated filter PHP source - */ - private function getFilter(array $filters, $level) - { - if (empty($filters)) { - return ''; - } - - $name = array_shift($filters); - $method = $this->getFindMethod($name); - $filter = ($method !== 'last') ? var_export($name, true) : ''; - $callable = $this->getCallable('$filter'); - $msg = var_export($name, true); - - return sprintf($this->prepare(self::FILTER, $level), $method, $filter, $callable, $msg, $this->getFilter($filters, $level)); - } - - const LINE = '$buffer .= "\n";'; - const TEXT = '$buffer .= %s%s;'; - - /** - * Generate Mustache Template output Buffer call PHP source. - * - * @param string $text - * @param int $level - * - * @return string Generated output Buffer call PHP source - */ - private function text($text, $level) - { - if ($text === "\n") { - $this->indentNextLine = true; - - return $this->prepare(self::LINE, $level); - } else { - return sprintf($this->prepare(self::TEXT, $level), $this->flushIndent(), var_export($text, true)); - } - } - - /** - * Prepare PHP source code snippet for output. - * - * @param string $text - * @param int $bonus Additional indent level (default: 0) - * @param boolean $prependNewline Prepend a newline to the snippet? (default: true) - * @param boolean $appendNewline Append a newline to the snippet? (default: false) - * - * @return string PHP source code snippet - */ - private function prepare($text, $bonus = 0, $prependNewline = true, $appendNewline = false) - { - $text = ($prependNewline ? "\n" : '').trim($text); - if ($prependNewline) { - $bonus++; - } - if ($appendNewline) { - $text .= "\n"; - } - - return preg_replace("/\n( {8})?/", "\n".str_repeat(" ", $bonus * 4), $text); - } - - const DEFAULT_ESCAPE = 'htmlspecialchars(%s, ENT_COMPAT, %s)'; - const CUSTOM_ESCAPE = 'call_user_func($this->mustache->getEscape(), %s)'; - - /** - * Get the current escaper. - * - * @param string $value (default: '$value') - * - * @return string Either a custom callback, or an inline call to `htmlspecialchars` - */ - private function getEscape($value = '$value') - { - if ($this->customEscape) { - return sprintf(self::CUSTOM_ESCAPE, $value); - } else { - return sprintf(self::DEFAULT_ESCAPE, $value, var_export($this->charset, true)); - } - } - - /** - * Select the appropriate Context `find` method for a given $id. - * - * The return value will be one of `find`, `findDot` or `last`. - * - * @see Mustache_Context::find - * @see Mustache_Context::findDot - * @see Mustache_Context::last - * - * @param string $id Variable name - * - * @return string `find` method name - */ - private function getFindMethod($id) - { - if ($id === '.') { - return 'last'; - } elseif (strpos($id, '.') === false) { - return 'find'; - } else { - return 'findDot'; - } - } - - const IS_CALLABLE = '!is_string(%s) && is_callable(%s)'; - const STRICT_IS_CALLABLE = 'is_object(%s) && is_callable(%s)'; - - private function getCallable($variable = '$value') - { - $tpl = $this->strictCallables ? self::STRICT_IS_CALLABLE : self::IS_CALLABLE; - - return sprintf($tpl, $variable, $variable); - } - - const LINE_INDENT = '$indent . '; - - /** - * Get the current $indent prefix to write to the buffer. - * - * @return string "$indent . " or "" - */ - private function flushIndent() - { - if ($this->indentNextLine) { - $this->indentNextLine = false; - - return self::LINE_INDENT; - } else { - return ''; - } - } -} diff --git a/core/lib/Mustache/Context.php b/core/lib/Mustache/Context.php deleted file mode 100644 index e7783b43c..000000000 --- a/core/lib/Mustache/Context.php +++ /dev/null @@ -1,149 +0,0 @@ -stack = array($context); - } - } - - /** - * Push a new Context frame onto the stack. - * - * @param mixed $value Object or array to use for context - */ - public function push($value) - { - array_push($this->stack, $value); - } - - /** - * Pop the last Context frame from the stack. - * - * @return mixed Last Context frame (object or array) - */ - public function pop() - { - return array_pop($this->stack); - } - - /** - * Get the last Context frame. - * - * @return mixed Last Context frame (object or array) - */ - public function last() - { - return end($this->stack); - } - - /** - * Find a variable in the Context stack. - * - * Starting with the last Context frame (the context of the innermost section), and working back to the top-level - * rendering context, look for a variable with the given name: - * - * * If the Context frame is an associative array which contains the key $id, returns the value of that element. - * * If the Context frame is an object, this will check first for a public method, then a public property named - * $id. Failing both of these, it will try `__isset` and `__get` magic methods. - * * If a value named $id is not found in any Context frame, returns an empty string. - * - * @param string $id Variable name - * - * @return mixed Variable value, or '' if not found - */ - public function find($id) - { - return $this->findVariableInStack($id, $this->stack); - } - - /** - * Find a 'dot notation' variable in the Context stack. - * - * Note that dot notation traversal bubbles through scope differently than the regular find method. After finding - * the initial chunk of the dotted name, each subsequent chunk is searched for only within the value of the previous - * result. For example, given the following context stack: - * - * $data = array( - * 'name' => 'Fred', - * 'child' => array( - * 'name' => 'Bob' - * ), - * ); - * - * ... and the Mustache following template: - * - * {{ child.name }} - * - * ... the `name` value is only searched for within the `child` value of the global Context, not within parent - * Context frames. - * - * @param string $id Dotted variable selector - * - * @return mixed Variable value, or '' if not found - */ - public function findDot($id) - { - $chunks = explode('.', $id); - $first = array_shift($chunks); - $value = $this->findVariableInStack($first, $this->stack); - - foreach ($chunks as $chunk) { - if ($value === '') { - return $value; - } - - $value = $this->findVariableInStack($chunk, array($value)); - } - - return $value; - } - - /** - * Helper function to find a variable in the Context stack. - * - * @see Mustache_Context::find - * - * @param string $id Variable name - * @param array $stack Context stack - * - * @return mixed Variable value, or '' if not found - */ - private function findVariableInStack($id, array $stack) - { - for ($i = count($stack) - 1; $i >= 0; $i--) { - if (is_object($stack[$i]) && !$stack[$i] instanceof Closure) { - if (method_exists($stack[$i], $id)) { - return $stack[$i]->$id(); - } elseif (isset($stack[$i]->$id)) { - return $stack[$i]->$id; - } - } elseif (is_array($stack[$i]) && array_key_exists($id, $stack[$i])) { - return $stack[$i][$id]; - } - } - - return ''; - } -} diff --git a/core/lib/Mustache/Engine.php b/core/lib/Mustache/Engine.php deleted file mode 100644 index 5f92d1948..000000000 --- a/core/lib/Mustache/Engine.php +++ /dev/null @@ -1,729 +0,0 @@ - '__MyTemplates_', - * - * // A cache directory for compiled templates. Mustache will not cache templates unless this is set - * 'cache' => dirname(__FILE__).'/tmp/cache/mustache', - * - * // Override default permissions for cache files. Defaults to using the system-defined umask. It is - * // *strongly* recommended that you configure your umask properly rather than overriding permissions here. - * 'cache_file_mode' => 0666, - * - * // A Mustache template loader instance. Uses a StringLoader if not specified. - * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), - * - * // A Mustache loader instance for partials. - * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), - * - * // An array of Mustache partials. Useful for quick-and-dirty string template loading, but not as - * // efficient or lazy as a Filesystem (or database) loader. - * 'partials' => array('foo' => file_get_contents(dirname(__FILE__).'/views/partials/foo.mustache')), - * - * // An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order - * // sections), or any other valid Mustache context value. They will be prepended to the context stack, - * // so they will be available in any template loaded by this Mustache instance. - * 'helpers' => array('i18n' => function($text) { - * // do something translatey here... - * }), - * - * // An 'escape' callback, responsible for escaping double-mustache variables. - * 'escape' => function($value) { - * return htmlspecialchars($buffer, ENT_COMPAT, 'UTF-8'); - * }, - * - * // Character set for `htmlspecialchars`. Defaults to 'UTF-8'. Use 'UTF-8'. - * 'charset' => 'ISO-8859-1', - * - * // A Mustache Logger instance. No logging will occur unless this is set. Using a PSR-3 compatible - * // logging library -- such as Monolog -- is highly recommended. A simple stream logger implementation is - * // available as well: - * 'logger' => new Mustache_Logger_StreamLogger('php://stderr'), - * - * // Only treat Closure instances and invokable classes as callable. If true, values like - * // `array('ClassName', 'methodName')` and `array($classInstance, 'methodName')`, which are traditionally - * // "callable" in PHP, are not called to resolve variables for interpolation or section contexts. This - * // helps protect against arbitrary code execution when user input is passed directly into the template. - * // This currently defaults to false, but will default to true in v3.0. - * 'strict_callables' => true, - * ); - * - * @throws Mustache_Exception_InvalidArgumentException If `escape` option is not callable. - * - * @param array $options (default: array()) - */ - public function __construct(array $options = array()) - { - if (isset($options['template_class_prefix'])) { - $this->templateClassPrefix = $options['template_class_prefix']; - } - - if (isset($options['cache'])) { - $this->cache = $options['cache']; - } - - if (isset($options['cache_file_mode'])) { - $this->cacheFileMode = $options['cache_file_mode']; - } - - if (isset($options['loader'])) { - $this->setLoader($options['loader']); - } - - if (isset($options['partials_loader'])) { - $this->setPartialsLoader($options['partials_loader']); - } - - if (isset($options['partials'])) { - $this->setPartials($options['partials']); - } - - if (isset($options['helpers'])) { - $this->setHelpers($options['helpers']); - } - - if (isset($options['escape'])) { - if (!is_callable($options['escape'])) { - throw new Mustache_Exception_InvalidArgumentException('Mustache Constructor "escape" option must be callable'); - } - - $this->escape = $options['escape']; - } - - if (isset($options['charset'])) { - $this->charset = $options['charset']; - } - - if (isset($options['logger'])) { - $this->setLogger($options['logger']); - } - - if (isset($options['strict_callables'])) { - $this->strictCallables = $options['strict_callables']; - } - } - - /** - * Shortcut 'render' invocation. - * - * Equivalent to calling `$mustache->loadTemplate($template)->render($context);` - * - * @see Mustache_Engine::loadTemplate - * @see Mustache_Template::render - * - * @param string $template - * @param mixed $context (default: array()) - * - * @return string Rendered template - */ - public function render($template, $context = array()) - { - return $this->loadTemplate($template)->render($context); - } - - /** - * Get the current Mustache escape callback. - * - * @return mixed Callable or null - */ - public function getEscape() - { - return $this->escape; - } - - /** - * Get the current Mustache character set. - * - * @return string - */ - public function getCharset() - { - return $this->charset; - } - - /** - * Set the Mustache template Loader instance. - * - * @param Mustache_Loader $loader - */ - public function setLoader(Mustache_Loader $loader) - { - $this->loader = $loader; - } - - /** - * Get the current Mustache template Loader instance. - * - * If no Loader instance has been explicitly specified, this method will instantiate and return - * a StringLoader instance. - * - * @return Mustache_Loader - */ - public function getLoader() - { - if (!isset($this->loader)) { - $this->loader = new Mustache_Loader_StringLoader; - } - - return $this->loader; - } - - /** - * Set the Mustache partials Loader instance. - * - * @param Mustache_Loader $partialsLoader - */ - public function setPartialsLoader(Mustache_Loader $partialsLoader) - { - $this->partialsLoader = $partialsLoader; - } - - /** - * Get the current Mustache partials Loader instance. - * - * If no Loader instance has been explicitly specified, this method will instantiate and return - * an ArrayLoader instance. - * - * @return Mustache_Loader - */ - public function getPartialsLoader() - { - if (!isset($this->partialsLoader)) { - $this->partialsLoader = new Mustache_Loader_ArrayLoader; - } - - return $this->partialsLoader; - } - - /** - * Set partials for the current partials Loader instance. - * - * @throws Mustache_Exception_RuntimeException If the current Loader instance is immutable - * - * @param array $partials (default: array()) - */ - public function setPartials(array $partials = array()) - { - if (!isset($this->partialsLoader)) { - $this->partialsLoader = new Mustache_Loader_ArrayLoader; - } - - if (!$this->partialsLoader instanceof Mustache_Loader_MutableLoader) { - throw new Mustache_Exception_RuntimeException('Unable to set partials on an immutable Mustache Loader instance'); - } - - $this->partialsLoader->setTemplates($partials); - } - - /** - * Set an array of Mustache helpers. - * - * An array of 'helpers'. Helpers can be global variables or objects, closures (e.g. for higher order sections), or - * any other valid Mustache context value. They will be prepended to the context stack, so they will be available in - * any template loaded by this Mustache instance. - * - * @throws Mustache_Exception_InvalidArgumentException if $helpers is not an array or Traversable - * - * @param array|Traversable $helpers - */ - public function setHelpers($helpers) - { - if (!is_array($helpers) && !$helpers instanceof Traversable) { - throw new Mustache_Exception_InvalidArgumentException('setHelpers expects an array of helpers'); - } - - $this->getHelpers()->clear(); - - foreach ($helpers as $name => $helper) { - $this->addHelper($name, $helper); - } - } - - /** - * Get the current set of Mustache helpers. - * - * @see Mustache_Engine::setHelpers - * - * @return Mustache_HelperCollection - */ - public function getHelpers() - { - if (!isset($this->helpers)) { - $this->helpers = new Mustache_HelperCollection; - } - - return $this->helpers; - } - - /** - * Add a new Mustache helper. - * - * @see Mustache_Engine::setHelpers - * - * @param string $name - * @param mixed $helper - */ - public function addHelper($name, $helper) - { - $this->getHelpers()->add($name, $helper); - } - - /** - * Get a Mustache helper by name. - * - * @see Mustache_Engine::setHelpers - * - * @param string $name - * - * @return mixed Helper - */ - public function getHelper($name) - { - return $this->getHelpers()->get($name); - } - - /** - * Check whether this Mustache instance has a helper. - * - * @see Mustache_Engine::setHelpers - * - * @param string $name - * - * @return boolean True if the helper is present - */ - public function hasHelper($name) - { - return $this->getHelpers()->has($name); - } - - /** - * Remove a helper by name. - * - * @see Mustache_Engine::setHelpers - * - * @param string $name - */ - public function removeHelper($name) - { - $this->getHelpers()->remove($name); - } - - /** - * Set the Mustache Logger instance. - * - * @throws Mustache_Exception_InvalidArgumentException If logger is not an instance of Mustache_Logger or Psr\Log\LoggerInterface. - * - * @param Mustache_Logger|Psr\Log\LoggerInterface $logger - */ - public function setLogger($logger = null) - { - if ($logger !== null && !($logger instanceof Mustache_Logger || is_a($logger, 'Psr\\Log\\LoggerInterface'))) { - throw new Mustache_Exception_InvalidArgumentException('Expected an instance of Mustache_Logger or Psr\\Log\\LoggerInterface.'); - } - - $this->logger = $logger; - } - - /** - * Get the current Mustache Logger instance. - * - * @return Mustache_Logger|Psr\Log\LoggerInterface - */ - public function getLogger() - { - return $this->logger; - } - - /** - * Set the Mustache Tokenizer instance. - * - * @param Mustache_Tokenizer $tokenizer - */ - public function setTokenizer(Mustache_Tokenizer $tokenizer) - { - $this->tokenizer = $tokenizer; - } - - /** - * Get the current Mustache Tokenizer instance. - * - * If no Tokenizer instance has been explicitly specified, this method will instantiate and return a new one. - * - * @return Mustache_Tokenizer - */ - public function getTokenizer() - { - if (!isset($this->tokenizer)) { - $this->tokenizer = new Mustache_Tokenizer; - } - - return $this->tokenizer; - } - - /** - * Set the Mustache Parser instance. - * - * @param Mustache_Parser $parser - */ - public function setParser(Mustache_Parser $parser) - { - $this->parser = $parser; - } - - /** - * Get the current Mustache Parser instance. - * - * If no Parser instance has been explicitly specified, this method will instantiate and return a new one. - * - * @return Mustache_Parser - */ - public function getParser() - { - if (!isset($this->parser)) { - $this->parser = new Mustache_Parser; - } - - return $this->parser; - } - - /** - * Set the Mustache Compiler instance. - * - * @param Mustache_Compiler $compiler - */ - public function setCompiler(Mustache_Compiler $compiler) - { - $this->compiler = $compiler; - } - - /** - * Get the current Mustache Compiler instance. - * - * If no Compiler instance has been explicitly specified, this method will instantiate and return a new one. - * - * @return Mustache_Compiler - */ - public function getCompiler() - { - if (!isset($this->compiler)) { - $this->compiler = new Mustache_Compiler; - } - - return $this->compiler; - } - - /** - * Helper method to generate a Mustache template class. - * - * @param string $source - * - * @return string Mustache Template class name - */ - public function getTemplateClassName($source) - { - return $this->templateClassPrefix . md5(sprintf( - 'version:%s,escape:%s,charset:%s,strict_callables:%s,source:%s', - self::VERSION, - isset($this->escape) ? 'custom' : 'default', - $this->charset, - $this->strictCallables ? 'true' : 'false', - $source - )); - } - - /** - * Load a Mustache Template by name. - * - * @param string $name - * - * @return Mustache_Template - */ - public function loadTemplate($name) - { - return $this->loadSource($this->getLoader()->load($name)); - } - - /** - * Load a Mustache partial Template by name. - * - * This is a helper method used internally by Template instances for loading partial templates. You can most likely - * ignore it completely. - * - * @param string $name - * - * @return Mustache_Template - */ - public function loadPartial($name) - { - try { - if (isset($this->partialsLoader)) { - $loader = $this->partialsLoader; - } elseif (isset($this->loader) && !$this->loader instanceof Mustache_Loader_StringLoader) { - $loader = $this->loader; - } else { - throw new Mustache_Exception_UnknownTemplateException($name); - } - - return $this->loadSource($loader->load($name)); - } catch (Mustache_Exception_UnknownTemplateException $e) { - // If the named partial cannot be found, log then return null. - $this->log( - Mustache_Logger::WARNING, - 'Partial not found: "{name}"', - array('name' => $e->getTemplateName()) - ); - } - } - - /** - * Load a Mustache lambda Template by source. - * - * This is a helper method used by Template instances to generate subtemplates for Lambda sections. You can most - * likely ignore it completely. - * - * @param string $source - * @param string $delims (default: null) - * - * @return Mustache_Template - */ - public function loadLambda($source, $delims = null) - { - if ($delims !== null) { - $source = $delims . "\n" . $source; - } - - return $this->loadSource($source); - } - - /** - * Instantiate and return a Mustache Template instance by source. - * - * @see Mustache_Engine::loadTemplate - * @see Mustache_Engine::loadPartial - * @see Mustache_Engine::loadLambda - * - * @param string $source - * - * @return Mustache_Template - */ - private function loadSource($source) - { - $className = $this->getTemplateClassName($source); - - if (!isset($this->templates[$className])) { - if (!class_exists($className, false)) { - if ($fileName = $this->getCacheFilename($source)) { - if (!is_file($fileName)) { - $this->log( - Mustache_Logger::DEBUG, - 'Writing "{className}" class to template cache: "{fileName}"', - array('className' => $className, 'fileName' => $fileName) - ); - - $this->writeCacheFile($fileName, $this->compile($source)); - } - - require_once $fileName; - } else { - $this->log( - Mustache_Logger::WARNING, - 'Template cache disabled, evaluating "{className}" class at runtime', - array('className' => $className) - ); - - eval('?>'.$this->compile($source)); - } - } - - $this->log( - Mustache_Logger::DEBUG, - 'Instantiating template: "{className}"', - array('className' => $className) - ); - - $this->templates[$className] = new $className($this); - } - - return $this->templates[$className]; - } - - /** - * Helper method to tokenize a Mustache template. - * - * @see Mustache_Tokenizer::scan - * - * @param string $source - * - * @return array Tokens - */ - private function tokenize($source) - { - return $this->getTokenizer()->scan($source); - } - - /** - * Helper method to parse a Mustache template. - * - * @see Mustache_Parser::parse - * - * @param string $source - * - * @return array Token tree - */ - private function parse($source) - { - return $this->getParser()->parse($this->tokenize($source)); - } - - /** - * Helper method to compile a Mustache template. - * - * @see Mustache_Compiler::compile - * - * @param string $source - * - * @return string generated Mustache template class code - */ - private function compile($source) - { - $tree = $this->parse($source); - $name = $this->getTemplateClassName($source); - - $this->log( - Mustache_Logger::INFO, - 'Compiling template to "{className}" class', - array('className' => $name) - ); - - return $this->getCompiler()->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables); - } - - /** - * Helper method to generate a Mustache Template class cache filename. - * - * @param string $source - * - * @return string Mustache Template class cache filename - */ - private function getCacheFilename($source) - { - if ($this->cache) { - return sprintf('%s/%s.php', $this->cache, $this->getTemplateClassName($source)); - } - } - - /** - * Helper method to dump a generated Mustache Template subclass to the file cache. - * - * @throws Mustache_Exception_RuntimeException if unable to create the cache directory or write to $fileName. - * - * @param string $fileName - * @param string $source - * - * @codeCoverageIgnore - */ - private function writeCacheFile($fileName, $source) - { - $dirName = dirname($fileName); - if (!is_dir($dirName)) { - $this->log( - Mustache_Logger::INFO, - 'Creating Mustache template cache directory: "{dirName}"', - array('dirName' => $dirName) - ); - - @mkdir($dirName, 0777, true); - if (!is_dir($dirName)) { - throw new Mustache_Exception_RuntimeException(sprintf('Failed to create cache directory "%s".', $dirName)); - } - - } - - $this->log( - Mustache_Logger::DEBUG, - 'Caching compiled template to "{fileName}"', - array('fileName' => $fileName) - ); - - $tempFile = tempnam($dirName, basename($fileName)); - if (false !== @file_put_contents($tempFile, $source)) { - if (@rename($tempFile, $fileName)) { - $mode = isset($this->cacheFileMode) ? $this->cacheFileMode : (0666 & ~umask()); - @chmod($fileName, $mode); - - return; - } - - $this->log( - Mustache_Logger::ERROR, - 'Unable to rename Mustache temp cache file: "{tempName}" -> "{fileName}"', - array('tempName' => $tempFile, 'fileName' => $fileName) - ); - } - - throw new Mustache_Exception_RuntimeException(sprintf('Failed to write cache file "%s".', $fileName)); - } - - /** - * Add a log record if logging is enabled. - * - * @param integer $level The logging level - * @param string $message The log message - * @param array $context The log context - */ - private function log($level, $message, array $context = array()) - { - if (isset($this->logger)) { - $this->logger->log($level, $message, $context); - } - } -} diff --git a/core/lib/Mustache/Exception.php b/core/lib/Mustache/Exception.php deleted file mode 100644 index b4f830046..000000000 --- a/core/lib/Mustache/Exception.php +++ /dev/null @@ -1,18 +0,0 @@ -token = $token; - parent::__construct($msg); - } - - public function getToken() - { - return $this->token; - } -} diff --git a/core/lib/Mustache/Exception/UnknownFilterException.php b/core/lib/Mustache/Exception/UnknownFilterException.php deleted file mode 100644 index f5c0884d4..000000000 --- a/core/lib/Mustache/Exception/UnknownFilterException.php +++ /dev/null @@ -1,29 +0,0 @@ -filterName = $filterName; - parent::__construct(sprintf('Unknown filter: %s', $filterName)); - } - - public function getFilterName() - { - return $this->filterName; - } -} diff --git a/core/lib/Mustache/Exception/UnknownHelperException.php b/core/lib/Mustache/Exception/UnknownHelperException.php deleted file mode 100644 index 98af13ebe..000000000 --- a/core/lib/Mustache/Exception/UnknownHelperException.php +++ /dev/null @@ -1,29 +0,0 @@ -helperName = $helperName; - parent::__construct(sprintf('Unknown helper: %s', $helperName)); - } - - public function getHelperName() - { - return $this->helperName; - } -} diff --git a/core/lib/Mustache/Exception/UnknownTemplateException.php b/core/lib/Mustache/Exception/UnknownTemplateException.php deleted file mode 100644 index 141d372bd..000000000 --- a/core/lib/Mustache/Exception/UnknownTemplateException.php +++ /dev/null @@ -1,29 +0,0 @@ -templateName = $templateName; - parent::__construct(sprintf('Unknown template: %s', $templateName)); - } - - public function getTemplateName() - { - return $this->templateName; - } -} diff --git a/core/lib/Mustache/HelperCollection.php b/core/lib/Mustache/HelperCollection.php deleted file mode 100644 index e9911378a..000000000 --- a/core/lib/Mustache/HelperCollection.php +++ /dev/null @@ -1,170 +0,0 @@ - $helper` pairs. - * - * @throws Mustache_Exception_InvalidArgumentException if the $helpers argument isn't an array or Traversable - * - * @param array|Traversable $helpers (default: null) - */ - public function __construct($helpers = null) - { - if ($helpers !== null) { - if (!is_array($helpers) && !$helpers instanceof Traversable) { - throw new Mustache_Exception_InvalidArgumentException('HelperCollection constructor expects an array of helpers'); - } - - foreach ($helpers as $name => $helper) { - $this->add($name, $helper); - } - } - } - - /** - * Magic mutator. - * - * @see Mustache_HelperCollection::add - * - * @param string $name - * @param mixed $helper - */ - public function __set($name, $helper) - { - $this->add($name, $helper); - } - - /** - * Add a helper to this collection. - * - * @param string $name - * @param mixed $helper - */ - public function add($name, $helper) - { - $this->helpers[$name] = $helper; - } - - /** - * Magic accessor. - * - * @see Mustache_HelperCollection::get - * - * @param string $name - * - * @return mixed Helper - */ - public function __get($name) - { - return $this->get($name); - } - - /** - * Get a helper by name. - * - * @throws Mustache_Exception_UnknownHelperException If helper does not exist. - * - * @param string $name - * - * @return mixed Helper - */ - public function get($name) - { - if (!$this->has($name)) { - throw new Mustache_Exception_UnknownHelperException($name); - } - - return $this->helpers[$name]; - } - - /** - * Magic isset(). - * - * @see Mustache_HelperCollection::has - * - * @param string $name - * - * @return boolean True if helper is present - */ - public function __isset($name) - { - return $this->has($name); - } - - /** - * Check whether a given helper is present in the collection. - * - * @param string $name - * - * @return boolean True if helper is present - */ - public function has($name) - { - return array_key_exists($name, $this->helpers); - } - - /** - * Magic unset(). - * - * @see Mustache_HelperCollection::remove - * - * @param string $name - */ - public function __unset($name) - { - $this->remove($name); - } - - /** - * Check whether a given helper is present in the collection. - * - * @throws Mustache_Exception_UnknownHelperException if the requested helper is not present. - * - * @param string $name - */ - public function remove($name) - { - if (!$this->has($name)) { - throw new Mustache_Exception_UnknownHelperException($name); - } - - unset($this->helpers[$name]); - } - - /** - * Clear the helper collection. - * - * Removes all helpers from this collection - */ - public function clear() - { - $this->helpers = array(); - } - - /** - * Check whether the helper collection is empty. - * - * @return boolean True if the collection is empty - */ - public function isEmpty() - { - return empty($this->helpers); - } -} diff --git a/core/lib/Mustache/LICENSE b/core/lib/Mustache/LICENSE deleted file mode 100644 index 6db530008..000000000 --- a/core/lib/Mustache/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) -Copyright (c) 2010 Justin Hileman - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE -OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/Mustache/LambdaHelper.php b/core/lib/Mustache/LambdaHelper.php deleted file mode 100644 index dfd4659c4..000000000 --- a/core/lib/Mustache/LambdaHelper.php +++ /dev/null @@ -1,49 +0,0 @@ -mustache = $mustache; - $this->context = $context; - } - - /** - * Render a string as a Mustache template with the current rendering context. - * - * @param string $string - * - * @return Rendered template. - */ - public function render($string) - { - return $this->mustache - ->loadLambda((string) $string) - ->renderInternal($this->context); - } -} diff --git a/core/lib/Mustache/Loader.php b/core/lib/Mustache/Loader.php deleted file mode 100644 index f659a1d3a..000000000 --- a/core/lib/Mustache/Loader.php +++ /dev/null @@ -1,28 +0,0 @@ - '{{ bar }}', - * 'baz' => 'Hey {{ qux }}!' - * ); - * - * $tpl = $loader->load('foo'); // '{{ bar }}' - * - * The ArrayLoader is used internally as a partials loader by Mustache_Engine instance when an array of partials - * is set. It can also be used as a quick-and-dirty Template loader. - */ -class Mustache_Loader_ArrayLoader implements Mustache_Loader, Mustache_Loader_MutableLoader -{ - - /** - * ArrayLoader constructor. - * - * @param array $templates Associative array of Template source (default: array()) - */ - public function __construct(array $templates = array()) - { - $this->templates = $templates; - } - - /** - * Load a Template. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) - { - if (!isset($this->templates[$name])) { - throw new Mustache_Exception_UnknownTemplateException($name); - } - - return $this->templates[$name]; - } - - /** - * Set an associative array of Template sources for this loader. - * - * @param array $templates - */ - public function setTemplates(array $templates) - { - $this->templates = $templates; - } - - /** - * Set a Template source by name. - * - * @param string $name - * @param string $template Mustache Template source - */ - public function setTemplate($name, $template) - { - $this->templates[$name] = $template; - } -} diff --git a/core/lib/Mustache/Loader/CascadingLoader.php b/core/lib/Mustache/Loader/CascadingLoader.php deleted file mode 100644 index 192edb97f..000000000 --- a/core/lib/Mustache/Loader/CascadingLoader.php +++ /dev/null @@ -1,69 +0,0 @@ -loaders = array(); - foreach ($loaders as $loader) { - $this->addLoader($loader); - } - } - - /** - * Add a Loader instance. - * - * @param Mustache_Loader $loader A Mustache Loader instance - */ - public function addLoader(Mustache_Loader $loader) - { - $this->loaders[] = $loader; - } - - /** - * Load a Template by name. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) - { - foreach ($this->loaders as $loader) { - try { - return $loader->load($name); - } catch (Mustache_Exception_UnknownTemplateException $e) { - // do nothing, check the next loader. - } - } - - throw new Mustache_Exception_UnknownTemplateException($name); - } -} diff --git a/core/lib/Mustache/Loader/FilesystemLoader.php b/core/lib/Mustache/Loader/FilesystemLoader.php deleted file mode 100644 index 71d7f1c8e..000000000 --- a/core/lib/Mustache/Loader/FilesystemLoader.php +++ /dev/null @@ -1,120 +0,0 @@ -load('foo'); // equivalent to `file_get_contents(dirname(__FILE__).'/views/foo.mustache'); - * - * This is probably the most useful Mustache Loader implementation. It can be used for partials and normal Templates: - * - * $m = new Mustache(array( - * 'loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'), - * 'partials_loader' => new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views/partials'), - * )); - */ -class Mustache_Loader_FilesystemLoader implements Mustache_Loader -{ - private $baseDir; - private $extension = '.mustache'; - private $templates = array(); - - /** - * Mustache filesystem Loader constructor. - * - * Passing an $options array allows overriding certain Loader options during instantiation: - * - * $options = array( - * // The filename extension used for Mustache templates. Defaults to '.mustache' - * 'extension' => '.ms', - * ); - * - * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. - * - * @param string $baseDir Base directory containing Mustache template files. - * @param array $options Array of Loader options (default: array()) - */ - public function __construct($baseDir, array $options = array()) - { - $this->baseDir = rtrim(realpath($baseDir), '/'); - - if (!is_dir($this->baseDir)) { - throw new Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); - } - - if (array_key_exists('extension', $options)) { - if (empty($options['extension'])) { - $this->extension = ''; - } else { - $this->extension = '.' . ltrim($options['extension'], '.'); - } - } - } - - /** - * Load a Template by name. - * - * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); - * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) - { - if (!isset($this->templates[$name])) { - $this->templates[$name] = $this->loadFile($name); - } - - return $this->templates[$name]; - } - - /** - * Helper function for loading a Mustache file by name. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - protected function loadFile($name) - { - $fileName = $this->getFileName($name); - - if (!file_exists($fileName)) { - throw new Mustache_Exception_UnknownTemplateException($name); - } - - return file_get_contents($fileName); - } - - /** - * Helper function for getting a Mustache template file name. - * - * @param string $name - * - * @return string Template file name - */ - protected function getFileName($name) - { - $fileName = $this->baseDir . '/' . $name; - if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { - $fileName .= $this->extension; - } - - return $fileName; - } -} diff --git a/core/lib/Mustache/Loader/InlineLoader.php b/core/lib/Mustache/Loader/InlineLoader.php deleted file mode 100644 index 1463bf8b5..000000000 --- a/core/lib/Mustache/Loader/InlineLoader.php +++ /dev/null @@ -1,121 +0,0 @@ -load('hello'); - * $goodbye = $loader->load('goodbye'); - * - * __halt_compiler(); - * - * @@ hello - * Hello, {{ planet }}! - * - * @@ goodbye - * Goodbye, cruel {{ planet }} - * - * Templates are deliniated by lines containing only `@@ name`. - * - * The InlineLoader is well-suited to micro-frameworks such as Silex: - * - * $app->register(new MustacheServiceProvider, array( - * 'mustache.loader' => new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__) - * )); - * - * $app->get('/{name}', function() use ($app) { - * return $app['mustache']->render('hello', compact('name')); - * }) - * ->value('name', 'world'); - * - * __halt_compiler(); - * - * @@ hello - * Hello, {{ name }}! - * - */ -class Mustache_Loader_InlineLoader implements Mustache_Loader -{ - protected $fileName; - protected $offset; - protected $templates; - - /** - * The InlineLoader requires a filename and offset to process templates. - * The magic constants `__FILE__` and `__COMPILER_HALT_OFFSET__` are usually - * perfectly suited to the job: - * - * $loader = new Mustache_Loader_InlineLoader(__FILE__, __COMPILER_HALT_OFFSET__); - * - * Note that this only works if the loader is instantiated inside the same - * file as the inline templates. If the templates are located in another - * file, it would be necessary to manually specify the filename and offset. - * - * @param string $fileName The file to parse for inline templates - * @param int $offset A string offset for the start of the templates. - * This usually coincides with the `__halt_compiler` - * call, and the `__COMPILER_HALT_OFFSET__`. - */ - public function __construct($fileName, $offset) - { - if (!is_file($fileName)) { - throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid filename.'); - } - - if (!is_int($offset) || $offset < 0) { - throw new Mustache_Exception_InvalidArgumentException('InlineLoader expects a valid file offset.'); - } - - $this->fileName = $fileName; - $this->offset = $offset; - } - - /** - * Load a Template by name. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) - { - $this->loadTemplates(); - - if (!array_key_exists($name, $this->templates)) { - throw new Mustache_Exception_UnknownTemplateException($name); - } - - return $this->templates[$name]; - } - - /** - * Parse and load templates from the end of a source file. - */ - protected function loadTemplates() - { - if ($this->templates === null) { - $this->templates = array(); - $data = file_get_contents($this->fileName, false, null, $this->offset); - foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) { - if (trim($chunk)) { - list($name, $content) = explode("\n", $chunk, 2); - $this->templates[trim($name)] = trim($content); - } - } - } - } -} diff --git a/core/lib/Mustache/Loader/MutableLoader.php b/core/lib/Mustache/Loader/MutableLoader.php deleted file mode 100644 index 02bb207a9..000000000 --- a/core/lib/Mustache/Loader/MutableLoader.php +++ /dev/null @@ -1,32 +0,0 @@ -load('{{ foo }}'); // '{{ foo }}' - * - * This is the default Template Loader instance used by Mustache: - * - * $m = new Mustache; - * $tpl = $m->loadTemplate('{{ foo }}'); - * echo $tpl->render(array('foo' => 'bar')); // "bar" - */ -class Mustache_Loader_StringLoader implements Mustache_Loader -{ - - /** - * Load a Template by source. - * - * @param string $name Mustache Template source - * - * @return string Mustache Template source - */ - public function load($name) - { - return $name; - } -} diff --git a/core/lib/Mustache/Logger.php b/core/lib/Mustache/Logger.php deleted file mode 100644 index e08359a91..000000000 --- a/core/lib/Mustache/Logger.php +++ /dev/null @@ -1,135 +0,0 @@ -log(Mustache_Logger::EMERGENCY, $message, $context); - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param array $context - */ - public function alert($message, array $context = array()) - { - $this->log(Mustache_Logger::ALERT, $message, $context); - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param array $context - */ - public function critical($message, array $context = array()) - { - $this->log(Mustache_Logger::CRITICAL, $message, $context); - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param array $context - */ - public function error($message, array $context = array()) - { - $this->log(Mustache_Logger::ERROR, $message, $context); - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param array $context - */ - public function warning($message, array $context = array()) - { - $this->log(Mustache_Logger::WARNING, $message, $context); - } - - /** - * Normal but significant events. - * - * @param string $message - * @param array $context - */ - public function notice($message, array $context = array()) - { - $this->log(Mustache_Logger::NOTICE, $message, $context); - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param array $context - */ - public function info($message, array $context = array()) - { - $this->log(Mustache_Logger::INFO, $message, $context); - } - - /** - * Detailed debug information. - * - * @param string $message - * @param array $context - */ - public function debug($message, array $context = array()) - { - $this->log(Mustache_Logger::DEBUG, $message, $context); - } -} diff --git a/core/lib/Mustache/Logger/StreamLogger.php b/core/lib/Mustache/Logger/StreamLogger.php deleted file mode 100644 index da771f90e..000000000 --- a/core/lib/Mustache/Logger/StreamLogger.php +++ /dev/null @@ -1,193 +0,0 @@ - 100, - self::INFO => 200, - self::NOTICE => 250, - self::WARNING => 300, - self::ERROR => 400, - self::CRITICAL => 500, - self::ALERT => 550, - self::EMERGENCY => 600, - ); - - protected $stream = null; - protected $url = null; - - /** - * @throws InvalidArgumentException if the logging level is unknown. - * - * @param string $stream Resource instance or URL - * @param integer $level The minimum logging level at which this handler will be triggered - */ - public function __construct($stream, $level = Mustache_Logger::ERROR) - { - $this->setLevel($level); - - if (is_resource($stream)) { - $this->stream = $stream; - } else { - $this->url = $stream; - } - } - - /** - * Close stream resources. - */ - public function __destruct() - { - if (is_resource($this->stream)) { - fclose($this->stream); - } - } - - /** - * Set the minimum logging level. - * - * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown. - * - * @param integer $level The minimum logging level which will be written - */ - public function setLevel($level) - { - if (!array_key_exists($level, self::$levels)) { - throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); - } - - $this->level = $level; - } - - /** - * Get the current minimum logging level. - * - * @return integer - */ - public function getLevel() - { - return $this->level; - } - - /** - * Logs with an arbitrary level. - * - * @throws Mustache_Exception_InvalidArgumentException if the logging level is unknown. - * - * @param mixed $level - * @param string $message - * @param array $context - */ - public function log($level, $message, array $context = array()) - { - if (!array_key_exists($level, self::$levels)) { - throw new Mustache_Exception_InvalidArgumentException(sprintf('Unexpected logging level: %s', $level)); - } - - if (self::$levels[$level] >= self::$levels[$this->level]) { - $this->writeLog($level, $message, $context); - } - } - - /** - * Write a record to the log. - * - * @throws Mustache_Exception_LogicException If neither a stream resource nor url is present. - * @throws Mustache_Exception_RuntimeException If the stream url cannot be opened. - * - * @param integer $level The logging level - * @param string $message The log message - * @param array $context The log context - */ - protected function writeLog($level, $message, array $context = array()) - { - if (!is_resource($this->stream)) { - if (!isset($this->url)) { - throw new Mustache_Exception_LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); - } - - $this->stream = fopen($this->url, 'a'); - if (!is_resource($this->stream)) { - // @codeCoverageIgnoreStart - throw new Mustache_Exception_RuntimeException(sprintf('The stream or file "%s" could not be opened.', $this->url)); - // @codeCoverageIgnoreEnd - } - } - - fwrite($this->stream, self::formatLine($level, $message, $context)); - } - - /** - * Gets the name of the logging level. - * - * @throws InvalidArgumentException if the logging level is unknown. - * - * @param integer $level - * - * @return string - */ - protected static function getLevelName($level) - { - return strtoupper($level); - } - - /** - * Format a log line for output. - * - * @param integer $level The logging level - * @param string $message The log message - * @param array $context The log context - * - * @return string - */ - protected static function formatLine($level, $message, array $context = array()) - { - return sprintf( - "%s: %s\n", - self::getLevelName($level), - self::interpolateMessage($message, $context) - ); - } - - /** - * Interpolate context values into the message placeholders. - * - * @param string $message - * @param array $context - * - * @return string - */ - protected static function interpolateMessage($message, array $context = array()) - { - if (strpos($message, '{') === false) { - return $message; - } - - // build a replacement array with braces around the context keys - $replace = array(); - foreach ($context as $key => $val) { - $replace['{' . $key . '}'] = $val; - } - - // interpolate replacement values into the the message and return - return strtr($message, $replace); - } -} diff --git a/core/lib/Mustache/Parser.php b/core/lib/Mustache/Parser.php deleted file mode 100644 index ab7db8463..000000000 --- a/core/lib/Mustache/Parser.php +++ /dev/null @@ -1,91 +0,0 @@ -buildTree(new ArrayIterator($tokens)); - } - - /** - * Helper method for recursively building a parse tree. - * - * @throws Mustache_Exception_SyntaxException when nesting errors or mismatched section tags are encountered. - * - * @param ArrayIterator $tokens Stream of Mustache tokens - * @param array $parent Parent token (default: null) - * - * @return array Mustache Token parse tree - */ - private function buildTree(ArrayIterator $tokens, array $parent = null) - { - $nodes = array(); - - do { - $token = $tokens->current(); - $tokens->next(); - - if ($token === null) { - continue; - } else { - switch ($token[Mustache_Tokenizer::TYPE]) { - case Mustache_Tokenizer::T_SECTION: - case Mustache_Tokenizer::T_INVERTED: - $nodes[] = $this->buildTree($tokens, $token); - break; - - case Mustache_Tokenizer::T_END_SECTION: - if (!isset($parent)) { - $msg = sprintf('Unexpected closing tag: /%s', $token[Mustache_Tokenizer::NAME]); - throw new Mustache_Exception_SyntaxException($msg, $token); - } - - if ($token[Mustache_Tokenizer::NAME] !== $parent[Mustache_Tokenizer::NAME]) { - $msg = sprintf('Nesting error: %s vs. %s', $parent[Mustache_Tokenizer::NAME], $token[Mustache_Tokenizer::NAME]); - throw new Mustache_Exception_SyntaxException($msg, $token); - } - - $parent[Mustache_Tokenizer::END] = $token[Mustache_Tokenizer::INDEX]; - $parent[Mustache_Tokenizer::NODES] = $nodes; - - return $parent; - break; - - default: - $nodes[] = $token; - break; - } - } - - } while ($tokens->valid()); - - if (isset($parent)) { - $msg = sprintf('Missing closing tag: %s', $parent[Mustache_Tokenizer::NAME]); - throw new Mustache_Exception_SyntaxException($msg, $parent); - } - - return $nodes; - } -} diff --git a/core/lib/Mustache/Template.php b/core/lib/Mustache/Template.php deleted file mode 100644 index aeee42d49..000000000 --- a/core/lib/Mustache/Template.php +++ /dev/null @@ -1,177 +0,0 @@ -mustache = $mustache; - } - - /** - * Mustache Template instances can be treated as a function and rendered by simply calling them: - * - * $m = new Mustache_Engine; - * $tpl = $m->loadTemplate('Hello, {{ name }}!'); - * echo $tpl(array('name' => 'World')); // "Hello, World!" - * - * @see Mustache_Template::render - * - * @param mixed $context Array or object rendering context (default: array()) - * - * @return string Rendered template - */ - public function __invoke($context = array()) - { - return $this->render($context); - } - - /** - * Render this template given the rendering context. - * - * @param mixed $context Array or object rendering context (default: array()) - * - * @return string Rendered template - */ - public function render($context = array()) - { - return $this->renderInternal($this->prepareContextStack($context)); - } - - /** - * Internal rendering method implemented by Mustache Template concrete subclasses. - * - * This is where the magic happens :) - * - * NOTE: This method is not part of the Mustache.php public API. - * - * @param Mustache_Context $context - * @param string $indent (default: '') - * - * @return string Rendered template - */ - abstract public function renderInternal(Mustache_Context $context, $indent = ''); - - /** - * Tests whether a value should be iterated over (e.g. in a section context). - * - * In most languages there are two distinct array types: list and hash (or whatever you want to call them). Lists - * should be iterated, hashes should be treated as objects. Mustache follows this paradigm for Ruby, Javascript, - * Java, Python, etc. - * - * PHP, however, treats lists and hashes as one primitive type: array. So Mustache.php needs a way to distinguish - * between between a list of things (numeric, normalized array) and a set of variables to be used as section context - * (associative array). In other words, this will be iterated over: - * - * $items = array( - * array('name' => 'foo'), - * array('name' => 'bar'), - * array('name' => 'baz'), - * ); - * - * ... but this will be used as a section context block: - * - * $items = array( - * 1 => array('name' => 'foo'), - * 'banana' => array('name' => 'bar'), - * 42 => array('name' => 'baz'), - * ); - * - * @param mixed $value - * - * @return boolean True if the value is 'iterable' - */ - protected function isIterable($value) - { - if (is_object($value)) { - return $value instanceof Traversable; - } elseif (is_array($value)) { - $i = 0; - foreach ($value as $k => $v) { - if ($k !== $i++) { - return false; - } - } - - return true; - } else { - return false; - } - } - - /** - * Helper method to prepare the Context stack. - * - * Adds the Mustache HelperCollection to the stack's top context frame if helpers are present. - * - * @param mixed $context Optional first context frame (default: null) - * - * @return Mustache_Context - */ - protected function prepareContextStack($context = null) - { - $stack = new Mustache_Context; - - $helpers = $this->mustache->getHelpers(); - if (!$helpers->isEmpty()) { - $stack->push($helpers); - } - - if (!empty($context)) { - $stack->push($context); - } - - return $stack; - } - - /** - * Resolve a context value. - * - * Invoke the value if it is callable, otherwise return the value. - * - * @param mixed $value - * @param Mustache_Context $context - * @param string $indent - * - * @return string - */ - protected function resolveValue($value, Mustache_Context $context, $indent = '') - { - if (($this->strictCallables ? is_object($value) : !is_string($value)) && is_callable($value)) { - return $this->mustache - ->loadLambda((string) call_user_func($value)) - ->renderInternal($context, $indent); - } - - return $value; - } -} diff --git a/core/lib/Mustache/Tokenizer.php b/core/lib/Mustache/Tokenizer.php deleted file mode 100644 index 56e8ba6b7..000000000 --- a/core/lib/Mustache/Tokenizer.php +++ /dev/null @@ -1,315 +0,0 @@ -'; - const T_PARTIAL_2 = '<'; - const T_DELIM_CHANGE = '='; - const T_ESCAPED = '_v'; - const T_UNESCAPED = '{'; - const T_UNESCAPED_2 = '&'; - const T_TEXT = '_t'; - const T_PRAGMA = '%'; - - // Valid token types - private static $tagTypes = array( - self::T_SECTION => true, - self::T_INVERTED => true, - self::T_END_SECTION => true, - self::T_COMMENT => true, - self::T_PARTIAL => true, - self::T_PARTIAL_2 => true, - self::T_DELIM_CHANGE => true, - self::T_ESCAPED => true, - self::T_UNESCAPED => true, - self::T_UNESCAPED_2 => true, - self::T_PRAGMA => true, - ); - - // Interpolated tags - private static $interpolatedTags = array( - self::T_ESCAPED => true, - self::T_UNESCAPED => true, - self::T_UNESCAPED_2 => true, - ); - - // Token properties - const TYPE = 'type'; - const NAME = 'name'; - const OTAG = 'otag'; - const CTAG = 'ctag'; - const INDEX = 'index'; - const END = 'end'; - const INDENT = 'indent'; - const NODES = 'nodes'; - const VALUE = 'value'; - - private $pragmas; - private $state; - private $tagType; - private $tag; - private $buffer; - private $tokens; - private $seenTag; - private $lineStart; - private $otag; - private $ctag; - - /** - * Scan and tokenize template source. - * - * @param string $text Mustache template source to tokenize - * @param string $delimiters Optionally, pass initial opening and closing delimiters (default: null) - * - * @return array Set of Mustache tokens - */ - public function scan($text, $delimiters = null) - { - $this->reset(); - - if ($delimiters = trim($delimiters)) { - list($otag, $ctag) = explode(' ', $delimiters); - $this->otag = $otag; - $this->ctag = $ctag; - } - - $len = strlen($text); - for ($i = 0; $i < $len; $i++) { - switch ($this->state) { - case self::IN_TEXT: - if ($this->tagChange($this->otag, $text, $i)) { - $i--; - $this->flushBuffer(); - $this->state = self::IN_TAG_TYPE; - } else { - $char = substr($text, $i, 1); - if ($char == "\n") { - $this->filterLine(); - } else { - $this->buffer .= $char; - } - } - break; - - case self::IN_TAG_TYPE: - - $i += strlen($this->otag) - 1; - $char = substr($text, $i + 1, 1); - if (isset(self::$tagTypes[$char])) { - $tag = $char; - $this->tagType = $tag; - } else { - $tag = null; - $this->tagType = self::T_ESCAPED; - } - - if ($this->tagType === self::T_DELIM_CHANGE) { - $i = $this->changeDelimiters($text, $i); - $this->state = self::IN_TEXT; - } elseif ($this->tagType === self::T_PRAGMA) { - $i = $this->addPragma($text, $i); - $this->state = self::IN_TEXT; - } else { - if ($tag !== null) { - $i++; - } - $this->state = self::IN_TAG; - } - $this->seenTag = $i; - break; - - default: - if ($this->tagChange($this->ctag, $text, $i)) { - $this->tokens[] = array( - self::TYPE => $this->tagType, - self::NAME => trim($this->buffer), - self::OTAG => $this->otag, - self::CTAG => $this->ctag, - self::INDEX => ($this->tagType == self::T_END_SECTION) ? $this->seenTag - strlen($this->otag) : $i + strlen($this->ctag) - ); - - $this->buffer = ''; - $i += strlen($this->ctag) - 1; - $this->state = self::IN_TEXT; - if ($this->tagType == self::T_UNESCAPED) { - if ($this->ctag == '}}') { - $i++; - } else { - // Clean up `{{{ tripleStache }}}` style tokens. - $lastName = $this->tokens[count($this->tokens) - 1][self::NAME]; - if (substr($lastName, -1) === '}') { - $this->tokens[count($this->tokens) - 1][self::NAME] = trim(substr($lastName, 0, -1)); - } - } - } - } else { - $this->buffer .= substr($text, $i, 1); - } - break; - } - } - - $this->filterLine(true); - - foreach ($this->pragmas as $pragma) { - array_unshift($this->tokens, array( - self::TYPE => self::T_PRAGMA, - self::NAME => $pragma, - )); - } - - return $this->tokens; - } - - /** - * Helper function to reset tokenizer internal state. - */ - private function reset() - { - $this->state = self::IN_TEXT; - $this->tagType = null; - $this->tag = null; - $this->buffer = ''; - $this->tokens = array(); - $this->seenTag = false; - $this->lineStart = 0; - $this->otag = '{{'; - $this->ctag = '}}'; - $this->pragmas = array(); - } - - /** - * Flush the current buffer to a token. - */ - private function flushBuffer() - { - if (!empty($this->buffer)) { - $this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => $this->buffer); - $this->buffer = ''; - } - } - - /** - * Test whether the current line is entirely made up of whitespace. - * - * @return boolean True if the current line is all whitespace - */ - private function lineIsWhitespace() - { - $tokensCount = count($this->tokens); - for ($j = $this->lineStart; $j < $tokensCount; $j++) { - $token = $this->tokens[$j]; - if (isset(self::$tagTypes[$token[self::TYPE]])) { - if (isset(self::$interpolatedTags[$token[self::TYPE]])) { - return false; - } - } elseif ($token[self::TYPE] == self::T_TEXT) { - if (preg_match('/\S/', $token[self::VALUE])) { - return false; - } - } - } - - return true; - } - - /** - * Filter out whitespace-only lines and store indent levels for partials. - * - * @param bool $noNewLine Suppress the newline? (default: false) - */ - private function filterLine($noNewLine = false) - { - $this->flushBuffer(); - if ($this->seenTag && $this->lineIsWhitespace()) { - $tokensCount = count($this->tokens); - for ($j = $this->lineStart; $j < $tokensCount; $j++) { - if ($this->tokens[$j][self::TYPE] == self::T_TEXT) { - if (isset($this->tokens[$j+1]) && $this->tokens[$j+1][self::TYPE] == self::T_PARTIAL) { - $this->tokens[$j+1][self::INDENT] = $this->tokens[$j][self::VALUE]; - } - - $this->tokens[$j] = null; - } - } - } elseif (!$noNewLine) { - $this->tokens[] = array(self::TYPE => self::T_TEXT, self::VALUE => "\n"); - } - - $this->seenTag = false; - $this->lineStart = count($this->tokens); - } - - /** - * Change the current Mustache delimiters. Set new `otag` and `ctag` values. - * - * @param string $text Mustache template source - * @param int $index Current tokenizer index - * - * @return int New index value - */ - private function changeDelimiters($text, $index) - { - $startIndex = strpos($text, '=', $index) + 1; - $close = '='.$this->ctag; - $closeIndex = strpos($text, $close, $index); - - list($otag, $ctag) = explode(' ', trim(substr($text, $startIndex, $closeIndex - $startIndex))); - $this->otag = $otag; - $this->ctag = $ctag; - - return $closeIndex + strlen($close) - 1; - } - - private function addPragma($text, $index) - { - $end = strpos($text, $this->ctag, $index); - $this->pragmas[] = trim(substr($text, $index + 2, $end - $index - 2)); - - return $end + strlen($this->ctag) - 1; - } - - /** - * Test whether it's time to change tags. - * - * @param string $tag Current tag name - * @param string $text Mustache template source - * @param int $index Current tokenizer index - * - * @return boolean True if this is a closing section tag - */ - private function tagChange($tag, $text, $index) - { - return substr($text, $index, strlen($tag)) === $tag; - } - - public function returnTokens() - { - return $this->tokens; - } -} diff --git a/core/lib/PatternLab/Builder.php b/core/lib/PatternLab/Builder.php deleted file mode 100644 index 69baf064d..000000000 --- a/core/lib/PatternLab/Builder.php +++ /dev/null @@ -1,329 +0,0 @@ -patternCSS = array(); - - // set-up the various attributes for rendering templates - Helper::setup(); - - } - - /** - * Finds Media Queries in CSS files in the source/css/ dir - * - * @return {Array} an array of the appropriate MQs - */ - protected function gatherMQs() { - - $mqs = array(); - - // iterate over all of the other files in the source directory - $directoryIterator = new \RecursiveDirectoryIterator(Config::$options["sourceDir"]); - $objects = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach ($objects as $name => $object) { - - if ($object->isFile() && ($object->getExtension() == "css")) { - - $data = file_get_contents($object->getPathname()); - preg_match_all("/(min|max)-width:([ ]+)?(([0-9]{1,5})(\.[0-9]{1,20}|)(px|em))/",$data,$matches); - foreach ($matches[3] as $match) { - if (!in_array($match,$mqs)) { - $mqs[] = $match; - } - } - - } - - } - - usort($mqs, "strnatcmp"); - - return $mqs; - - } - - protected function generateAnnotations() { - - $annotations = array(); - $annotations["comments"] = array(); - - // iterate over all of the files in the annotations dir - $directoryIterator = new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_annotations"); - $objects = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach ($objects as $name => $object) { - - // if it's an .md file parse and generate the proper info - if ($object->isFile() && ($object->getExtension() == "md")) { - - $data = array(); - $data[0] = array(); - - $text = file_get_contents($object->getPathname()); - list($yaml,$markdown) = Documentation::parse($text); - - if (isset($yaml["el"]) || isset($yaml["selector"])) { - $data[0]["el"] = (isset($yaml["el"])) ? $yaml["el"] : $yaml["selector"]; - } else { - $data[0]["el"] = "#someimpossibleselector"; - } - $data[0]["title"] = isset($yaml["title"]) ? $yaml["title"] : ""; - $data[0]["comment"] = $markdown; - - $annotations["comments"] = array_merge($annotations["comments"],$data); - - } - - } - - // read in the old style annotations.js, modify the data and generate JSON array to merge - if (file_exists(Config::$options["sourceDir"]."/_annotations/annotations.js")) { - $text = file_get_contents(Config::$options["sourceDir"]."/_annotations/annotations.js"); - $text = str_replace("var comments = ","",$text); - $text = rtrim($text,";"); - $data = json_decode($text,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg("_annotations/annotations.js",$jsonErrorMessage,$data); - } - } - - // merge in any data from the old file - $annotations["comments"] = array_merge($annotations["comments"],$data["comments"]); - - // encode the content so it can be written out - $json = json_encode($annotations); - - // make sure annotations/ exists - if (!is_dir(Config::$options["publicDir"]."/annotations")) { - mkdir(Config::$options["publicDir"]."/annotations"); - } - - // write out the new annotations.js file - file_put_contents(Config::$options["publicDir"]."/annotations/annotations.js","var comments = ".$json.";"); - - } - - /** - * Generates the index page and style guide - */ - protected function generateIndex() { - - // grab the items for the nav - $niExporter = new NavItemsExporter(); - $navItems = $niExporter->run(); - - // grab the pattern paths that will be used on the front-end - $ppdExporter = new PatternPathDestsExporter(); - $patternPathDests = $ppdExporter->run(); - - // grab the view all paths that will be used on the front-end - $vapExporter = new ViewAllPathsExporter(); - $viewAllPaths = $vapExporter->run($navItems); - - // add the various configuration options that need to be drawn on the front-end - $navItems["autoreloadnav"] = Config::$options["autoReloadNav"]; - $navItems["autoreloadport"] = Config::$options["autoReloadPort"]; - $navItems["cacheBuster"] = Config::$options["cacheBuster"]; - $navItems["ipaddress"] = getHostByName(getHostName()); - $navItems["ishminimum"] = Config::$options["ishMinimum"]; - $navItems["ishmaximum"] = Config::$options["ishMaximum"]; - $navItems["ishControlsHide"] = Config::$options["ishControlsHide"]; - $navItems["mqs"] = $this->gatherMQs(); - $navItems["pagefollownav"] = Config::$options["pageFollowNav"]; - $navItems["pagefollowport"] = Config::$options["pageFollowPort"]; - $navItems["patternpaths"] = json_encode($patternPathDests); - $navItems["qrcodegeneratoron"] = Config::$options["qrCodeGeneratorOn"]; - $navItems["viewallpaths"] = json_encode($viewAllPaths); - $navItems["xiphostname"] = Config::$options["xipHostname"]; - - // render the index page - $index = Helper::$filesystemLoader->render('index',$navItems); - file_put_contents(Config::$options["publicDir"]."/index.html",$index); - - } - - /** - * Generates all of the patterns and puts them in the public directory - */ - protected function generatePatterns() { - - // make sure patterns exists - if (!is_dir(Config::$options["publicDir"]."/patterns")) { - mkdir(Config::$options["publicDir"]."/patterns"); - } - - // loop over the pattern data store to render the individual patterns - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (!$patternStoreData["hidden"])) { - - $path = $patternStoreData["pathDash"]; - $pathName = (isset($patternStoreData["pseudo"])) ? $patternStoreData["pathOrig"] : $patternStoreData["pathName"]; - - // modify the pattern mark-up - $markup = $patternStoreData["code"]; - $markupEncoded = htmlentities($markup); - $markupFull = $patternStoreData["header"].$markup.$patternStoreData["footer"]; - $markupEngine = htmlentities(file_get_contents(__DIR__.Config::$options["patternSourceDir"].$pathName.".".Config::$options["patternExtension"])); - - // if the pattern directory doesn't exist create it - if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$path)) { - mkdir(__DIR__.Config::$options["patternPublicDir"].$path); - } - - // write out the various pattern files - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".html",$markupFull); - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".escaped.html",$markupEncoded); - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".".Config::$options["patternExtension"],$markupEngine); - if (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) { - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$path."/".$path.".css",htmlentities($this->patternCSS[$p])); - } - - } - - } - - } - - /** - * Generates the style guide view - */ - protected function generateStyleguide() { - - if (!is_dir(Config::$options["publicDir"]."/styleguide/html/")) { - - print "ERROR: the main style guide wasn't written out. make sure public/styleguide exists. can copy core/styleguide\n"; - - } else { - - // grab the partials into a data object for the style guide - $ppExporter = new PatternPartialsExporter(); - $partialsAll = $ppExporter->run(); - - // render the style guide - $styleGuideHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); - $styleGuideFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); - $styleGuidePage = $styleGuideHead.Helper::$filesystemLoader->render("viewall",$partialsAll).$styleGuideFoot; - - file_put_contents(Config::$options["publicDir"]."/styleguide/html/styleguide.html",$styleGuidePage); - - } - - } - - /** - * Generates the view all pages - */ - protected function generateViewAllPages() { - - $viewAllHead = Helper::$htmlLoader->render(Helper::$mainPageHead,Data::$store); - $viewAllFoot = Helper::$htmlLoader->render(Helper::$mainPageFoot,Data::$store); - - // add view all to each list - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if ($patternStoreData["category"] == "patternSubtype") { - - // grab the partials into a data object for the style guide - $ppExporter = new PatternPartialsExporter(); - $partials = $ppExporter->run($patternStoreData["type"],$patternStoreData["name"]); - - if (!empty($partials["partials"])) { - - $partials["patternPartial"] = "viewall-".$patternStoreData["typeDash"]."-".$patternStoreData["nameDash"]; - - $viewAllPage = $viewAllHead.Helper::$filesystemLoader->render("viewall",$partials).$viewAllFoot; - - // if the pattern directory doesn't exist create it - $patternPath = $patternStoreData["pathDash"]; - if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$patternPath)) { - mkdir(__DIR__.Config::$options["patternPublicDir"].$patternPath); - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); - } else { - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); - } - - } - - } else if (($patternStoreData["category"] == "patternType") && PatternData::hasPatternSubtype($patternStoreData["nameDash"])) { - - // grab the partials into a data object for the style guide - $ppExporter = new PatternPartialsExporter(); - $partials = $ppExporter->run($patternStoreData["name"]); - - if (!empty($partials["partials"])) { - - $partials["patternPartial"] = "viewall-".$patternStoreData["nameDash"]."-all"; - - $viewAllPage = $viewAllHead.Helper::$filesystemLoader->render("viewall",$partials).$viewAllFoot; - - // if the pattern directory doesn't exist create it - $patternPath = $patternStoreData["pathDash"]; - if (!is_dir(__DIR__.Config::$options["patternPublicDir"].$patternPath)) { - mkdir(__DIR__.Config::$options["patternPublicDir"].$patternPath); - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); - } else { - file_put_contents(__DIR__.Config::$options["patternPublicDir"].$patternPath."/index.html",$viewAllPage); - } - - } - - } - - } - - } - - /** - * Loads the CSS from source/css/ into CSS Rule Saver to be used for code view - * Will eventually get pushed elsewhere - */ - protected function initializeCSSRuleSaver() { - - $loader = new \SplClassLoader('CSSRuleSaver', __DIR__.'/../../lib'); - $loader->register(); - - $this->cssRuleSaver = new \CSSRuleSaver\CSSRuleSaver; - - foreach(glob(Config::$options["sourceDir"]."/css/*.css") as $filename) { - $this->cssRuleSaver->loadCSS($filename); - } - - } - -} diff --git a/core/lib/PatternLab/Config.php b/core/lib/PatternLab/Config.php deleted file mode 100644 index 444c0ff65..000000000 --- a/core/lib/PatternLab/Config.php +++ /dev/null @@ -1,159 +0,0 @@ -migrate(true); - if ($migrate) { - if (!@copy(self::$plConfigPath, self::$userConfigPath)) { - print "Please make sure that Pattern Lab can write a new config to config/.\n"; - exit; - } - } else { - self::$options = self::writeNewConfigFile(self::$options,$defaultOptions); - } - } - - // making sure the config isn't empty - if (empty(self::$options)) { - print "A set of configuration options is required to use Pattern Lab.\n"; - exit; - } - - // set-up the source & public dirs - self::$options["sourceDir"] = rtrim(self::$options["sourceDir"],"\\"); - self::$options["publicDir"] = rtrim(self::$options["publicDir"],"\\"); - self::$options["patternSourceDir"] = "/../../../".self::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; - self::$options["patternPublicDir"] = "/../../../".self::$options["publicDir"]."/patterns".DIRECTORY_SEPARATOR; - self::$options["sourceDir"] = __DIR__."/../../../".self::$options["sourceDir"]; - self::$options["publicDir"] = __DIR__."/../../../".self::$options["publicDir"]; - - // populate some standard variables out of the config - foreach (self::$options as $key => $value) { - - // if the variables are array-like make sure the properties are validated/trimmed/lowercased before saving - if (in_array($key,self::$cleanValues)) { - $values = explode(",",$value); - array_walk($values,'PatternLab\Util::trim'); - self::$options[$key] = $values; - } else if ($key == "ishControlsHide") { - self::$options[$key] = new \stdClass(); - $class = self::$options[$key]; - if ($value != "") { - $values = explode(",",$value); - foreach($values as $value2) { - $value2 = trim($value2); - $class->$value2 = true; - } - } - if (self::$options["pageFollowNav"] == "false") { - $value = "tools-follow"; - $class->$value = true; - } - if (self::$options["autoReloadNav"] == "false") { - $value = "tools-reload"; - $class->$value = true; - } - $toolssnapshot = "tools-snapshot"; // i was an idgit and used dashes - if (!isset($class->$toolssnapshot)) { - if (!is_dir(self::$options["patternSourceDir"]."/snapshots")) { - $class->$toolssnapshot = true; - } - } - } - - } - - // set the cacheBuster - self::$options["cacheBuster"] = (self::$options["cacheBusterOn"] == "false") ? 0 : time(); - - // provide the default for enable CSS. performance hog so it should be run infrequently - self::$options["enableCSS"] = false; - - } - - /** - * Use the default config as a base and update it with old config options. Write out a new user config. - * @param {Array} the old configuration file options - * @param {Array} the default configuration file options - * - * @return {Array} the new configuration - */ - protected static function writeNewConfigFile($oldOptions,$defaultOptions) { - - // iterate over the old config and replace values in the new config - foreach ($oldOptions as $key => $value) { - if ($key != "v") { - $defaultOptions[$key] = $value; - } - } - - // create the output data - $configOutput = ""; - foreach ($defaultOptions as $key => $value) { - $configOutput .= $key." = \"".$value."\"\n"; - } - - // write out the new config file - file_put_contents(self::$userConfigPath,$configOutput); - - return $defaultOptions; - - } - -} diff --git a/core/lib/PatternLab/Console.php b/core/lib/PatternLab/Console.php deleted file mode 100644 index d30e4a33b..000000000 --- a/core/lib/PatternLab/Console.php +++ /dev/null @@ -1,351 +0,0 @@ -self = $_SERVER["PHP_SELF"]; - } - - /** - * Get the arguments that have been passed to the script via the commmand line - */ - public function getArguments() { - if (php_sapi_name() != 'cli') { - print "The builder script can only be run from the command line.\n"; - exit; - } - $this->options = getopt($this->optionsShort,$this->optionsLong); - } - - /** - * See if a particular command was passed to the script via the command line and return a boolean. Can either be the short or long version - * @param {String} list of arguments to check - * - * @return {Boolean} if the command has been passed to the script via the command line - */ - public function findCommand($args) { - $args = explode("|",$args); - foreach ($args as $arg) { - if (isset($this->options[$arg])) { - return true; - - } - } - return false; - } - - /** - * See if a particular command was passed to the script via the command line and return a value. Can either be the short or long version - * @param {String} list of arguments to check - * - * @return {String} the value that was passed via the command line - */ - public function findCommandValue($args) { - $args = explode("|",$args); - foreach ($args as $arg) { - if (isset($this->options[$arg])) { - return $this->options[$arg]; - } - } - return false; - } - - /** - * Find the short command for a given long gommand - * @param {String} long command to search for - * - * @return {String} the search command - */ - public function findCommandShort($arg) { - foreach ($this->commands as $command => $commandOptions) { - if ($commandOptions["commandLong"] == $arg) { - return $command; - } - } - return false; - } - - /** - * Return the command that was given in the command line arguments - * - * @return {String} the command. passes false if no command was found - */ - public function getCommand() { - foreach ($this->commands as $command => $attributes) { - if (isset($this->options[$command]) || isset($this->options[$attributes["commandLong"]])) { - return $command; - } - } - return false; - } - - /** - * Set-up the command so it can be used from the command line - * @param {String} the single character version of the command - * @param {String} the long version of the command - * @param {String} the description to be used in the "available commands" section of writeHelp() - * @param {String} the description to be used in the "help" section of writeHelpCommand() - */ - public function setCommand($short,$long,$desc,$help) { - $this->optionsShort .= $short; - $this->optionsLong[] = $long; - $short = str_replace(":","",$short); - $long = str_replace(":","",$long); - $this->commands[$short] = array("commandShort" => $short, "commandLong" => $long, "commandLongLength" => strlen($long), "commandDesc" => $desc, "commandHelp" => $help, "commandOptions" => array(), "commandExamples" => array()); - } - - /** - * Set a sample for a specific command - * @param {String} the single character of the command that this option is related to - * @param {String} the sample to be used in the "sample" section of writeHelpCommand() - * @param {String} the extra info to be used in the example command for the "sample" section of writeHelpCommand() - */ - public function setCommandSample($command,$sample,$extra) { - $this->commands[$command]["commandExamples"][] = array("exampleSample" => $sample, "exampleExtra" => $extra); - } - - /** - * See if a particular option was passed to the script via the command line and return a boolean. Can either be the short or long version - * @param {String} list of arguments to check - * - * @return {Boolean} if the command has been passed to the script via the command line - */ - public function findCommandOption($args) { - $args = explode("|",$args); - foreach ($args as $arg) { - if (isset($this->options[$arg])) { - return true; - } - } - return false; - } - - /** - * See if a particular option was passed to the script via the command line and return a value. Can either be the short or long version - * @param {String} list of arguments to check - * - * @return {String} the value that was passed via the command line - */ - public function findCommandOptionValue($args) { - $args = explode("|",$args); - foreach ($args as $arg) { - if (isset($this->options[$arg])) { - return $this->options[$arg]; - } - } - return false; - } - - /** - * Set-up an option for a given command so it can be used from the command line - * @param {String} the single character of the command that this option is related to - * @param {String} the single character version of the option - * @param {String} the long version of the option - * @param {String} the description to be used in the "available options" section of writeHelpCommand() - * @param {String} the sample to be used in the "sample" section of writeHelpCommand() - * @param {String} the extra info to be used in the example command for the "sample" section of writeHelpCommand() - */ - public function setCommandOption($command,$short,$long,$desc,$sample,$extra = "") { - if (strpos($this->optionsShort,$short) === false) { - $this->optionsShort .= $short; - } - if (!in_array($long,$this->optionsLong)) { - $this->optionsLong[] = $long; - } - $short = str_replace(":","",$short); - $long = str_replace(":","",$long); - $this->commands[$command]["commandOptions"][$short] = array("optionShort" => $short, "optionLong" => $long, "optionLongLength" => strlen($long), "optionDesc" => $desc, "optionSample" => $sample, "optionExtra" => $extra); - } - - /** - * Write out the generic help - */ - public function writeHelp() { - - /* - - The generic help follows this format: - - Pattern Lab Console Options - - Usage: - php core/console command [options] - - Available commands: - --build (-b) Build Pattern Lab - --watch (-w) Build Pattern Lab and watch for changes and rebuild as necessary - --version (-v) Display the version number - --help (-h) Display this help message. - - */ - - // find length of longest command - $lengthLong = 0; - foreach ($this->commands as $command => $attributes) { - $lengthLong = ($attributes["commandLongLength"] > $lengthLong) ? $attributes["commandLongLength"] : $lengthLong; - } - - // write out the generic usage info - $this->writeLine("Pattern Lab Console Options",true); - $this->writeLine("Usage:"); - $this->writeLine(" php ".$this->self." command [options]",true); - $this->writeLine("Available commands:"); - - // write out the commands - foreach ($this->commands as $command => $attributes) { - $spacer = $this->getSpacer($lengthLong,$attributes["commandLongLength"]); - $this->writeLine(" --".$attributes["commandLong"].$spacer."(-".$attributes["commandShort"].") ".$attributes["commandDesc"]); - } - - $this->writeLine(""); - - } - - /** - * Write out the command-specific help - * @param {String} the single character of the command that this option is related to - */ - public function writeHelpCommand($command = "") { - - /* - - The command help follows this format: - - Build Command Options - - Usage: - php core/console --build [--patternsonly|-p] [--nocache|-n] [--enablecss|-c] - - Available options: - --patternsonly (-p) Build only the patterns. Does NOT clean public/. - --nocache (-n) Set the cacheBuster value to 0. - --enablecss (-c) Generate CSS for each pattern. Resource intensive. - --help (-h) Display this help message. - - Help: - The build command builds an entire site a single time. It compiles the patterns and moves content from source/ into public/ - - Samples: - - To run and generate the CSS for each pattern: - php core/console build -c - - To build only the patterns and not move other files from source/ to public/ - php core/console build -p - - To turn off the cacheBuster - php core/console build -n - */ - - // if given an empty command or the command doesn't exist in the lists give the generic help - if (empty($command)) { - $this->writeHelp(); - return; - } - - $commandShort = $this->commands[$command]["commandShort"]; - $commandLong = $this->commands[$command]["commandLong"]; - $commandHelp = $this->commands[$command]["commandHelp"]; - $commandExtra = isset($this->commands[$command]["commandExtra"]) ? $this->commands[$command]["commandExtra"] : ""; - $commandOptions = $this->commands[$command]["commandOptions"]; - $commandExamples = $this->commands[$command]["commandExamples"]; - - $commandLongUC = ucfirst($commandLong); - - // write out the option list and get the longest item - $optionList = ""; - $lengthLong = 0; - foreach ($commandOptions as $option => $attributes) { - $optionList .= "[--".$attributes["optionLong"]."|-".$attributes["optionShort"]."] "; - $lengthLong = ($attributes["optionLongLength"] > $lengthLong) ? $attributes["optionLongLength"] : $lengthLong; - } - - // write out the generic usage info - $this->writeLine($commandLongUC." Command Options",true); - $this->writeLine("Usage:"); - $this->writeLine(" php ".$this->self." --".$commandLong."|-".$commandShort." ".$optionList,true); - - // write out the available options - if (count($commandOptions) > 0) { - $this->writeLine("Available options:"); - foreach ($commandOptions as $option => $attributes) { - $spacer = $this->getSpacer($lengthLong,$attributes["optionLongLength"]); - $this->writeLine(" --".$attributes["optionLong"].$spacer."(-".$attributes["optionShort"].") ".$attributes["optionDesc"]); - } - $this->writeLine(""); - } - - $this->writeLine("Help:"); - $this->writeLine(" ".$commandHelp,true); - - // write out the samples - if ((count($commandOptions) > 0) || (count($commandExamples) > 0)) { - $this->writeLine(" Samples:",true); - } - - if (count($commandExamples) > 0) { - foreach ($commandExamples as $example => $attributes) { - $this->writeLine(" ".$attributes["exampleSample"]); - $this->writeLine(" php ".$this->self." --".$commandLong." ".$attributes["exampleExtra"]); - $this->writeLine(" php ".$this->self." -".$commandShort." ".$attributes["exampleExtra"],true); - } - } - - if (count($commandOptions) > 0) { - foreach ($commandOptions as $option => $attributes) { - $this->writeLine(" ".$attributes["optionSample"]); - $this->writeLine(" php ".$this->self." --".$commandLong." --".$attributes["optionLong"]." ".$attributes["optionExtra"]); - $this->writeLine(" php ".$this->self." -".$commandShort." -".$attributes["optionShort"]." ".$attributes["optionExtra"],true); - } - } - - } - - /** - * Write out a line of the help - * @param {Boolean} handle double-break - */ - protected function writeLine($line,$doubleBreak = false) { - $break = ($doubleBreak) ? "\n\n" : "\n"; - print $line.$break; - } - - /** - * Make sure the space is properly set between long command options and short command options - * @param {Integer} the longest length of the command's options - * @param {Integer} the character length of the given option - */ - protected function getSpacer($lengthLong,$itemLongLength) { - $i = 0; - $spacer = " "; - $spacerLength = $lengthLong - $itemLongLength; - while ($i < $spacerLength) { - $spacer .= " "; - $i++; - } - return $spacer; - } - -} diff --git a/core/lib/PatternLab/Data.php b/core/lib/PatternLab/Data.php deleted file mode 100644 index 271ab13d9..000000000 --- a/core/lib/PatternLab/Data.php +++ /dev/null @@ -1,231 +0,0 @@ -setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach ($objects as $name => $object) { - - $ext = $object->getExtension(); - $data = array(); - $fileName = $object->getFilename(); - $hidden = ($fileName[0] == "_"); - $isFile = $object->isFile(); - $isListItems = strpos("listitems",$fileName); - $pathName = $object->getPathname(); - $pathNameClean = str_replace(Config::$options["sourceDir"]."/","",$pathName); - - if ($isFile && !$hidden && (($ext == "json") || ($ext == "yaml"))) { - - if ($isListItems === false) { - - if ($ext == "json") { - - $file = file_get_contents($pathName); - $data = json_decode($file,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($pathNameClean,$jsonErrorMessage,$data); - } - - } else if ($ext == "yaml") { - - $file = file_get_contents($pathName); - $data = Yaml::parse($file); - - } - - self::$store = array_replace_recursive(self::$store,$data); - - } else if ($isListItems !== false) { - - $data = ($ext == "json") ? self::getListItems("data/listitems.json") : self::getListItems("data/listitems.yaml","yaml"); - - if (!isset(self::$store["listItems"])) { - self::$store["listItems"] = array(); - } - - self::$store["listItems"] = array_replace_recursive(self::$store["listItems"],$data); - - } - - } - - } - - if (is_array(self::$store)) { - foreach (self::$reservedKeys as $reservedKey) { - if (array_key_exists($reservedKey,self::$store)) { - print "\"".$reservedKey."\" is a reserved key in Pattern Lab. The data using that key in _data.json will be overwritten. Please choose a new key.\n"; - } - } - } - - self::$store["cacheBuster"] = Config::$options["cacheBuster"]; - self::$store["link"] = array(); - self::$store["patternSpecific"] = array(); - - } - - /** - * Generate the listItems array - * @param {String} the filename for the pattern to be parsed - * @param {String} the extension so that a flag switch can be used to parse data - * - * @return {Array} the final set of list items - */ - protected static function getListItems($filepath,$ext = "json") { - - $listItems = array(); - $listItemsData = array(); - - // add list item data, makes 'listItems' a reserved word - if (file_exists(Config::$options["sourceDir"]."/".$filepath)) { - - $file = file_get_contents(Config::$options["sourceDir"]."/".$filepath); - - if ($ext == "json") { - $listItemsData = json_decode($file, true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($filepath,$jsonErrorMessage,$listItems); - } - } else { - $listItemsData = Yaml::parse($file); - } - - - $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); - - $i = 0; - $k = 1; - $c = count($listItemsData)+1; - - while ($k < $c) { - - shuffle($listItemsData); - $itemsArray = array(); - - while ($i < $k) { - $itemsArray[] = $listItemsData[$i]; - $i++; - } - - $listItems[$numbers[$k-1]] = $itemsArray; - - $i = 0; - $k++; - - } - - } - - return $listItems; - - } - - /** - * Get the final data array specifically for a pattern - * @param {String} the filename for the pattern to be parsed - * @param {Array} any extra data that should be added to the pattern specific data that's being returned - * - * @return {Array} the final set of list items - */ - public static function getPatternSpecificData($patternPartial,$extraData = array()) { - - // if there is pattern-specific data make sure to override the default in $this->d - $d = self::copy(); - - if (isset($d["patternSpecific"]) && array_key_exists($patternPartial,$d["patternSpecific"])) { - - if (!empty($d["patternSpecific"][$patternPartial]["data"])) { - $d = array_replace_recursive($d, $d["patternSpecific"][$patternPartial]["data"]); - } - - if (!empty($d["patternSpecific"][$patternPartial]["listItems"])) { - - $numbers = array("one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); - - $k = 0; - $c = count($d["patternSpecific"][$patternPartial]["listItems"]); - - while ($k < $c) { - $section = $numbers[$k]; - $d["listItems"][$section] = array_replace_recursive( $d["listItems"][$section], $d["patternSpecific"][$patternPartial]["listItems"][$section]); - $k++; - } - - } - - } - - if (!empty($extraData)) { - $d = array_replace_recursive($d, $extraData); - } - - unset($d["patternSpecific"]); - - return $d; - - } - - /** - * Print out the data var. For debugging purposes - * - * @return {String} the formatted version of the d object - */ - public static function printData() { - print_r(self::$store); - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/FileUtil.php b/core/lib/PatternLab/FileUtil.php deleted file mode 100644 index 7ef13d61b..000000000 --- a/core/lib/PatternLab/FileUtil.php +++ /dev/null @@ -1,62 +0,0 @@ -enableCSS = true; - - // initialize CSS rule saver - $this->initializeCSSRuleSaver(); - print "CSS generation enabled. This could take a few seconds...\n"; - - } - - // gather up all of the data to be used in patterns - Data::gather(); - - // gather all of the various pattern info - PatternData::gather(); - - // clean the public directory to remove old files - if ((Config::$options["cleanPublic"] == "true") && $moveStatic) { - Util::cleanPublic(); - } - - // render out the index and style guide - $this->generateIndex(); - $this->generateStyleguide(); - $this->generateViewAllPages(); - - // render out the patterns and move them to public/patterns - $this->generatePatterns(); - - // render the annotations as a js file - $this->generateAnnotations(); - - // move all of the files unless pattern only is set - if ($moveStatic) { - - // iterate over all of the other files in the source directory - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/"), \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace(Config::$options["sourceDir"].DIRECTORY_SEPARATOR,"",$name); - - if (($fileName[0] != "_") && (!in_array($object->getExtension(),Config::$options["ie"])) && (!in_array($object->getFilename(),Config::$options["id"]))) { - - // catch directories that have the ignored dir in their path - $ignoreDir = FileUtil::ignoreDir($fileName); - - // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !is_dir(Config::$options["publicDir"]."/".$fileName)) { - mkdir(Config::$options["publicDir"]."/".$fileName); - } - - // check to see if it's a new file or a file that has changed - if (!$ignoreDir && $object->isFile() && (!file_exists(Config::$options["publicDir"]."/".$fileName))) { - FileUtil::moveStaticFile($fileName); - } - - } - - } - - } - - // update the change time so the auto-reload will fire (doesn't work for the index and style guide) - Util::updateChangeTime(); - - print "your site has been generated...\n"; - - // print out how long it took to generate the site - if ($timePL) { - $mtime = microtime(); - $mtime = explode(" ",$mtime); - $mtime = $mtime[1] + $mtime[0]; - $endtime = $mtime; - $totaltime = ($endtime - $starttime); - $mem = round((memory_get_peak_usage(true)/1024)/1024,2); - print "site generation took ".$totaltime." seconds and used ".$mem."MB of memory...\n"; - } - - } - - /** - * Randomly prints a saying after the generate is complete - */ - public function printSaying() { - - $randomNumber = rand(0,60); - $sayings = array( - "have fun storming the castle", - "be well, do good work, and keep in touch", - "may the sun shine, all day long", - "smile", - "namaste", - "walk as if you are kissing the earth with your feet", - "to be beautiful means to be yourself", - "i was thinking of the immortal words of socrates, who said \"...i drank what?\"", - "let me take this moment to compliment you on your fashion sense, particularly your slippers", - "42", - "he who controls the spice controls the universe", - "the greatest thing you'll ever learn is just to love and be loved in return", - "nice wand", - "i don't have time for a grudge match with every poseur in a parka" - ); - if (isset($sayings[$randomNumber])) { - print $sayings[$randomNumber]."...\n"; - } - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/JSON.php b/core/lib/PatternLab/JSON.php deleted file mode 100644 index b0bd0553b..000000000 --- a/core/lib/PatternLab/JSON.php +++ /dev/null @@ -1,53 +0,0 @@ - false, - JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', - JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', - JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', - JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', - JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded' - ); - - /** - * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 - * @param {String} the file that generated the error - */ - public static function hasError() { - $error = json_last_error(); - $errorMessage = array_key_exists($error, self::$errors) ? self::$errors[$error] : "Unknown error ({$error})"; - return $errorMessage; - } - - /** - * Returns the last error message when building a JSON file. Mimics json_last_error_msg() from PHP 5.5 - * @param {String} the file that generated the error - */ - public static function lastErrorMsg($file,$message,$data) { - print "\nThe JSON file, ".$file.", wasn't loaded. The error: ".$message."\n"; - if ($message == "Syntax error, malformed JSON") { - print "\n"; - $parser = new JsonLint\JsonParser(); - $error = $parser->lint($data); - print $error->getMessage(); - print "\n\n"; - } - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/Migrator.php b/core/lib/PatternLab/Migrator.php deleted file mode 100644 index 2e75f7fa9..000000000 --- a/core/lib/PatternLab/Migrator.php +++ /dev/null @@ -1,141 +0,0 @@ -getFilename(); - if (!$migration->isDot() && $migration->isFile() && ($filename[0] != "_")) { - $migrationsValid[] = $filename; - } - } - - asort($migrationsValid); - - foreach ($migrationsValid as $filename) { - - $basePath = __DIR__."/../../../"; - $migrationData = json_decode(file_get_contents(__DIR__."/../../migrations/".$filename)); - $checkType = $migrationData->checkType; - $sourcePath = ($checkType == "fileExists") ? $basePath.$migrationData->sourcePath : $basePath.$migrationData->sourcePath.DIRECTORY_SEPARATOR; - $destinationPath = ($checkType == "fileExists") ? $basePath.$migrationData->destinationPath : $basePath.$migrationData->destinationPath.DIRECTORY_SEPARATOR; - - if ($checkType == "dirEmpty") { - - $emptyDir = true; - $objects = new \DirectoryIterator($destinationPath); - foreach ($objects as $object) { - if (!$object->isDot() && ($object->getFilename() != "README") && ($object->getFilename() != ".DS_Store")) { - $emptyDir = false; - } - } - - if ($emptyDir) { - $this->runMigration($filename, $sourcePath, $destinationPath, false); - } - - } else if ($checkType == "dirExists") { - - if (!is_dir($destinationPath)) { - mkdir($destinationPath); - } - - } else if ($checkType == "fileExists") { - - if (!file_exists($destinationPath)) { - $this->runMigration($filename, $sourcePath, $destinationPath, true); - } - - } else if (($checkType == "versionDiffDir") && $diffVersion) { - - // make sure the destination path exists - if (!is_dir($destinationPath)) { - mkdir($destinationPath); - } - - $this->runMigration($filename, $sourcePath, $destinationPath, false); - - } else if (($checkType == "versionDiffFile") && $diffVersion) { - - $this->runMigration($filename, $sourcePath, $destinationPath, true); - - } else { - - print "Pattern Lab doesn't recognize a checkType of ".$checkType.". The migrator class is pretty thin at the moment.\n"; - exit; - - } - - } - - } - - /** - * Run any migrations found in core/migrations that match the approved types - * @param {String} the filename of the migration - * @param {String} the path of the source directory - * @param {String} the path to the destination - * @param {Boolean} moving a single file or a directory - */ - protected function runMigration($filename, $sourcePath, $destinationPath, $singleFile) { - - $filename = str_replace(".json","",$filename); - print " Starting the ".$filename." migration...\n"; - - if ($singleFile) { - - copy($sourcePath.$fileName,$destinationPath.$fileName); - - } else { - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($sourcePath), \RecursiveIteratorIterator::SELF_FIRST); - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach ($objects as $object) { - - // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace($sourcePath,"",$object->getPathname()); - - // check to see if it's a new directory - if ($object->isDir() && !is_dir($destinationPath.$fileName)) { - mkdir($destinationPath.$fileName); - } else if ($object->isFile()) { - copy($sourcePath.$fileName,$destinationPath.$fileName); - } - - } - - } - - print " Completed the ".$filename." migration...\n"; - - } - -} diff --git a/core/lib/PatternLab/Parsers/Documentation.php b/core/lib/PatternLab/Parsers/Documentation.php deleted file mode 100644 index d832ba431..000000000 --- a/core/lib/PatternLab/Parsers/Documentation.php +++ /dev/null @@ -1,125 +0,0 @@ - $patternStoreData) { - if (($patternStoreData["category"] == "patternSubtype") && ($patternStoreData["typeDash"] == $patternType)) { - return true; - } - } - return false; - } - - /** - * Gather all of the information related to the patterns - */ - public static function gather($options = array()) { - - // load up the rules for parsing patterns and the directories - self::loadRules($options); - - // iterate over the patterns & related data and regenerate the entire site if they've changed - $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(__DIR__.Config::$options["patternSourceDir"]), \RecursiveIteratorIterator::SELF_FIRST); - $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); - - $patternObjects = iterator_to_array($patternObjects); - ksort($patternObjects); - - foreach ($patternObjects as $name => $object) { - - $ext = $object->getExtension(); - $isDir = $object->isDir(); - $isFile = $object->isFile(); - - $path = str_replace(__DIR__.Config::$options["patternSourceDir"],"",$object->getPath()); - $pathName = str_replace(__DIR__.Config::$options["patternSourceDir"],"",$object->getPathname()); - $name = $object->getFilename(); - $depth = substr_count($pathName,DIRECTORY_SEPARATOR); - - // iterate over the rules and see if the current file matches one, if so run the rule - foreach (self::$rules as $rule) { - if ($rule->test($depth, $ext, $isDir, $isFile, $name)) { - $rule->run($depth, $ext, $path, $pathName, $name); - } - } - - } - - // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns - $dataLinkExporter = new DataLinkExporter(); - $dataLinkExporter->run(); - - // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns - $dataMergeExporter = new DataMergeExporter(); - $dataMergeExporter->run(); - - // add the lineage info to PatternData::$store - $lineageHelper = new LineageHelper(); - $lineageHelper->run(); - - // using the lineage info update the pattern states on PatternData::$store - $patternStateHelper = new PatternStateHelper(); - $patternStateHelper->run(); - - // set-up code pattern paths - $ppdExporter = new PatternPathSrcExporter(); - $patternPathSrc = $ppdExporter->run(); - $options = array(); - $options["patternPaths"] = $patternPathSrc; - - // render out all of the patterns and store the generated info in PatternData::$store - $patternCodeHelper = new PatternCodeHelper($options); - $patternCodeHelper->run(); - - // loop through and check KSS (this will change in the future) - $KSSHelper = new KSSHelperPlugin($options); - $KSSHelper->run(); - - } - - /** - * Load all of the rules related to Pattern Data - */ - public static function loadRules($options) { - foreach (glob(__DIR__."/PatternData/Rules/*.php") as $filename) { - $rule = str_replace(".php","",str_replace(__DIR__."/PatternData/Rules/","",$filename)); - $ruleClass = "\PatternLab\PatternData\Rules\\".$rule; - self::$rules[] = new $ruleClass($options); - } - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Exporter.php b/core/lib/PatternLab/PatternData/Exporter.php deleted file mode 100644 index 0094d51da..000000000 --- a/core/lib/PatternLab/PatternData/Exporter.php +++ /dev/null @@ -1,21 +0,0 @@ - $patternStoreData) { - - if ($patternStoreData["category"] == "pattern") { - - Data::$store["link"][$patternStoreKey] = "../../".$patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"; - - } - - } - - } - -} diff --git a/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php b/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php deleted file mode 100644 index ec377a875..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/DataMergeExporter.php +++ /dev/null @@ -1,54 +0,0 @@ - $patternStoreData) { - - if ($patternStoreData["category"] == "pattern") { - - if (isset($patternStoreData["data"]) || isset($patternStoreData["listItems"])) { - Data::$store["patternSpecific"][$patternStoreKey] = array(); - } - - if (isset($patternStoreData["data"])) { - Data::$store["patternSpecific"][$patternStoreKey]["data"] = $patternStoreData["data"]; - } - - if (isset($patternStoreData["listItems"])) { - Data::$store["patternSpecific"][$patternStoreKey]["listItems"] = $patternStoreData["listItems"]; - } - - } - - } - - // walk across the data and change link.pattern-partial to real source - array_walk_recursive(Data::$store,'\PatternLab\Util::compareReplace'); - - } - -} diff --git a/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php b/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php deleted file mode 100644 index 365902032..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/NavItemsExporter.php +++ /dev/null @@ -1,156 +0,0 @@ - $patternStoreData) { - - if ($patternStoreData["category"] == "patternType") { - - $bi = (count($navItems["patternTypes"]) == 0) ? 0 : $bi + 1; - - // add a new patternType to the nav - $navItems["patternTypes"][$bi] = array("patternTypeLC" => strtolower($patternStoreData["nameClean"]), - "patternTypeUC" => ucwords($patternStoreData["nameClean"]), - "patternType" => $patternStoreData["name"], - "patternTypeDash" => $patternStoreData["nameDash"], - "patternTypeItems" => array(), - "patternItems" => array()); - - // starting a new set of pattern types. it might not have any pattern subtypes - $patternSubtypeSet = false; - $patternType = $patternStoreData["name"]; - $patternTypeDash = $patternStoreData["nameDash"]; - - } else if ($patternStoreData["category"] == "patternSubtype") { - - $ni = (!$patternSubtypeSet) ? 0 : $ni + 1; - - // add a new patternSubtype to the nav - $navItems["patternTypes"][$bi]["patternTypeItems"][$ni] = array("patternSubtypeLC" => strtolower($patternStoreData["nameClean"]), - "patternSubtypeUC" => ucwords($patternStoreData["nameClean"]), - "patternSubtype" => $patternStoreData["name"], - "patternSubtypeDash" => $patternStoreData["nameDash"], - "patternSubtypeItems" => array()); - - // starting a new set of pattern types. it might not have any pattern subtypes - $patternSubtype = $patternStoreData["name"]; - $patternSubtypeDash = $patternStoreData["nameDash"]; - $patternSubtypeSet = true; - - } else if ($patternStoreData["category"] == "pattern") { - - if (!$patternStoreData["hidden"]) { - - // set-up the info for the nav - $patternInfo = array("patternPath" => $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html", - "patternSrcPath" => $patternStoreData["pathName"], - "patternName" => ucwords($patternStoreData["nameClean"]), - "patternState" => $patternStoreData["state"], - "patternPartial" => $patternStoreData["partial"]); - - // add to the nav - if ($patternStoreData["depth"] == 1) { - $navItems["patternTypes"][$bi]["patternItems"][] = $patternInfo; - } else { - $navItems["patternTypes"][$bi]["patternTypeItems"][$ni]["patternSubtypeItems"][] = $patternInfo; - } - - } - - } - - } - - // review each subtype. add a view all link or remove the subtype as necessary - foreach ($navItems["patternTypes"] as $patternTypeKey => $patternTypeValues) { - - $reset = false; - $patternType = $patternTypeValues["patternType"]; - $patternTypeDash = $patternTypeValues["patternTypeDash"]; - - if (!in_array($patternType,Config::$options["styleGuideExcludes"])) { - - foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { - - // if there are no sub-items in a section remove it - if (empty($patternSubtypeValues["patternSubtypeItems"])) { - - unset($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]); - $reset = true; - - } else { - - $patternSubtype = $patternSubtypeValues["patternSubtype"]; - $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; - $subItemsCount = count($patternSubtypeValues["patternSubtypeItems"]); - - // add a view all link - $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"][$patternSubtypeKey]["patternSubtypeItems"][$subItemsCount] = array( - "patternPath" => $patternType."-".$patternSubtype."/index.html", - "patternName" => "View All", - "patternType" => $patternType, - "patternSubtype" => $patternSubtype, - "patternPartial" => "viewall-".$patternTypeDash."-".$patternSubtypeDash); - - } - - } - - } - - if ($reset) { - $navItems["patternTypes"][$patternTypeKey]["patternTypeItems"] = array_values($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"]); - $reset = false; - } - - // add an overall view all link to the menus with sub-menus - if (!empty($navItems["patternTypes"][$patternTypeKey]["patternTypeItems"])) { - - $navItems["patternTypes"][$patternTypeKey]["patternItems"][] = array("patternPath" => $patternType."/index.html", - "patternName" => "View All", - "patternType" => $patternType, - "patternSubtype" => "all", - "patternPartial" => "viewall-".$patternTypeDash."-all"); - - } - - } - - return $navItems; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php deleted file mode 100644 index 91193d57d..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPartialsExporter.php +++ /dev/null @@ -1,79 +0,0 @@ - $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (!$patternStoreData["hidden"]) && ($patternStoreData["depth"] == 2) && (!in_array($patternStoreData["type"],Config::$options["styleGuideExcludes"]))) { - - if ((($patternStoreData["type"] == $type) && empty($subtype)) || (empty($type) && empty($subtype)) || (($patternStoreData["type"] == $type) && ($patternStoreData["subtype"] == $subtype))) { - - $patternPartialData = array(); - $patternPartialData["patternName"] = ucwords($patternStoreData["nameClean"]); - $patternPartialData["patternLink"] = $patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"; - $patternPartialData["patternPartial"] = $patternStoreData["partial"]; - $patternPartialData["patternPartialCode"] = $patternStoreData["code"]; - - $patternPartialData["patternLineageExists"] = isset($patternStoreData["lineages"]); - $patternPartialData["patternLineages"] = isset($patternStoreData["lineages"]) ? $patternStoreData["lineages"] : array(); - $patternPartialData["patternLineageRExists"] = isset($patternStoreData["lineagesR"]); - $patternPartialData["patternLineagesR"] = isset($patternStoreData["lineagesR"]) ? $patternStoreData["lineagesR"] : array(); - $patternPartialData["patternLineageEExists"] = (isset($patternStoreData["lineages"]) || isset($patternStoreData["lineagesR"])); - - $patternPartialData["patternDescExists"] = isset($patternStoreData["desc"]); - $patternPartialData["patternDesc"] = isset($patternStoreData["desc"]) ? $patternStoreData["desc"] : ""; - - $patternPartialData["patternModifiersExist"] = isset($patternStoreData["modifiers"]); - $patternPartialData["patternModifiers"] = isset($patternStoreData["modifiers"]) ? $patternStoreData["modifiers"] : array(); - - $patternPartialData["patternCSSExists"] = Config::$options["enableCSS"]; - - $patternPartials[] = $patternPartialData; - - } - - } - - } - - return array("partials" => $patternPartials); - - } - -} - - diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php deleted file mode 100644 index 29a50d40b..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPathDestsExporter.php +++ /dev/null @@ -1,51 +0,0 @@ - $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { - - $nameDash = $patternStoreData["nameDash"]; - $typeDash = $patternStoreData["typeDash"]; - - if (!isset($patternPathDests[$typeDash])) { - $patternPathDests[$typeDash] = array(); - } - - $patternPathDests[$typeDash][$nameDash] = $patternStoreData["pathDash"]; - - } - - } - - return $patternPathDests; - - } - -} diff --git a/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php b/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php deleted file mode 100644 index d8050a613..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/PatternPathSrcExporter.php +++ /dev/null @@ -1,52 +0,0 @@ - $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { - - $nameDash = $patternStoreData["nameDash"]; - $typeDash = $patternStoreData["typeDash"]; - - if (!isset($patternPathDests[$typeDash])) { - $patternPathDests[$typeDash] = array(); - } - - $patternPathDests[$typeDash][$nameDash] = $patternStoreData["pathName"]; - - } - - } - - return $patternPathDests; - - } - -} diff --git a/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php b/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php deleted file mode 100644 index 18d30f9b9..000000000 --- a/core/lib/PatternLab/PatternData/Exporters/ViewAllPathsExporter.php +++ /dev/null @@ -1,72 +0,0 @@ - $patternTypeValues) { - - $patternType = $patternTypeValues["patternType"]; - $patternTypeDash = $patternTypeValues["patternTypeDash"]; - - if (!in_array($patternType,Config::$options["styleGuideExcludes"])) { - - foreach ($patternTypeValues["patternTypeItems"] as $patternSubtypeKey => $patternSubtypeValues) { - - $patternSubtype = $patternSubtypeValues["patternSubtype"]; - $patternSubtypeDash = $patternSubtypeValues["patternSubtypeDash"]; - - if (isset($patternSubtypeValues["patternSubtypeItems"])) { - - foreach ($patternSubtypeValues["patternSubtypeItems"] as $patternSubtypeItemKey => $patternSubtypeItemValues) { - - if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { - - $viewAllPaths[$patternTypeDash][$patternSubtypeDash] = $patternType."-".$patternSubtype; - - } - - } - - } - - if (strpos($patternSubtypeItemValues["patternPartial"],"viewall-") !== false) { - - $viewAllPaths[$patternTypeDash]["all"] = $patternType; - - } - - } - - } - - } - - return $viewAllPaths; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Helper.php b/core/lib/PatternLab/PatternData/Helper.php deleted file mode 100644 index 558624e9b..000000000 --- a/core/lib/PatternLab/PatternData/Helper.php +++ /dev/null @@ -1,21 +0,0 @@ - $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"]))) { - - $patternLineages = array(); - $fileName = $patternStoreData["pathName"].".".Config::$options["patternExtension"]; - $fileNameFull = __DIR__."/../..".Config::$options["patternSourceDir"].$fileName; - - if (file_exists($fileNameFull)) { - $foundLineages = $this->findLineages($fileNameFull); - } - - if (!empty($foundLineages)) { - - foreach ($foundLineages as $lineage) { - - if (isset(PatternData::$store[$lineage])) { - - $patternLineages[] = array("lineagePattern" => $lineage, - "lineagePath" => "../../patterns/".$patternStoreData["pathDash"]."/".$patternStoreData["pathDash"].".html"); - - } else { - - if (strpos($lineage, '/') === false) { - print "You may have a typo in ".$fileName.". {{> ".$lineage." }} is not a valid pattern.\n"; - } - - } - - } - - // add the lineages to the PatternData::$store - PatternData::$store[$patternStoreKey]["lineages"] = $patternLineages; - - } - - } - - } - - // handle all of those pseudo patterns - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { - - // add the lineages to the PatternData::$store - $patternStoreKeyOriginal = $patternStoreData["original"]; - PatternData::$store[$patternStoreKey]["lineages"] = PatternData::$store[$patternStoreKeyOriginal]["lineages"]; - - } - - } - - // check for the reverse lineages and skip pseudo patterns - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (!isset($patternStoreData["pseudo"]))) { - - $patternLineagesR = array(); - - foreach (PatternData::$store as $haystackKey => $haystackData) { - - if (($haystackData["category"] == "pattern") && (isset($haystackData["lineages"]))) { - - foreach ($haystackData["lineages"] as $haystackLineage) { - - if ($haystackLineage["lineagePattern"] == $patternStoreData["partial"]) { - - $foundAlready = false; - foreach ($patternLineagesR as $patternCheck) { - - if ($patternCheck["lineagePattern"] == $patternStoreData["partial"]) { - $foundAlready = true; - break; - } - - } - - if (!$foundAlready) { - - if (isset(PatternData::$store[$haystackKey])) { - - $path = PatternData::$store[$haystackKey]["pathDash"]; - $patternLineagesR[] = array("lineagePattern" => $haystackKey, - "lineagePath" => "../../patterns/".$path."/".$path.".html"); - - } - - } - - } - - } - - } - - } - - PatternData::$store[$patternStoreKey]["lineagesR"] = $patternLineagesR; - - } - - } - - // handle all of those pseudo patterns - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && (isset($patternStoreData["pseudo"]))) { - - // add the lineages to the PatternData::$store - $patternStoreKeyOriginal = $patternStoreData["original"]; - PatternData::$store[$patternStoreKey]["lineagesR"] = PatternData::$store[$patternStoreKeyOriginal]["lineagesR"]; - - } - - } - - } - - - /** - * Get the lineage for a given pattern by parsing it and matching mustache partials - * @param {String} the filename for the pattern to be parsed - * - * @return {Array} a list of patterns - */ - protected function findLineages($filename) { - $data = file_get_contents($filename); - if (preg_match_all('/{{>([ ]+)?([A-Za-z0-9-_]+)(?:\:[A-Za-z0-9-]+)?(?:(| )\(.*)?([ ]+)?}}/',$data,$matches)) { - return array_unique($matches[2]); - } - return array(); - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php b/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php deleted file mode 100644 index 6530e304e..000000000 --- a/core/lib/PatternLab/PatternData/Helpers/PatternCodeHelper.php +++ /dev/null @@ -1,72 +0,0 @@ -patternPaths = $options["patternPaths"]; - - } - - public function run() { - - $options = array(); - $options["patternPaths"] = $this->patternPaths; - PatternEngine::setup($options); - - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if (($patternStoreData["category"] == "pattern") && !$patternStoreData["hidden"]) { - - $patternFooterData = array("patternFooterData" => array()); - //$patternFooterData["patternFooterData"]["cssEnabled"] = (Config::$options["enableCSS"] && isset($this->patternCSS[$p])) ? "true" : "false"; - $patternFooterData["patternFooterData"]["lineage"] = isset($patternStoreData["lineages"]) ? json_encode($patternStoreData["lineages"]) : "[]"; - $patternFooterData["patternFooterData"]["lineageR"] = isset($patternStoreData["lineagesR"]) ? json_encode($patternStoreData["lineagesR"]) : "[]"; - $patternFooterData["patternFooterData"]["patternBreadcrumb"] = $patternStoreData["breadcrumb"]; - $patternFooterData["patternFooterData"]["patternDesc"] = (isset($patternStoreData["desc"])) ? $patternStoreData["desc"] : ""; - $patternFooterData["patternFooterData"]["patternExtension"] = Config::$options["patternExtension"]; - $patternFooterData["patternFooterData"]["patternModifiers"] = (isset($patternStoreData["modifiers"])) ? json_encode($patternStoreData["modifiers"]) : "[]"; - $patternFooterData["patternFooterData"]["patternName"] = $patternStoreData["nameClean"]; - $patternFooterData["patternFooterData"]["patternPartial"] = $patternStoreData["partial"]; - $patternFooterData["patternFooterData"]["patternState"] = $patternStoreData["state"]; - - $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; - - $data = Data::getPatternSpecificData($patternStoreKey,$patternFooterData); - - $header = Render::Header(Helper::$patternHead,$data); - $code = Render::Pattern($srcPath,$data); - $footer = Render::Footer(Helper::$patternFoot,$data); - - PatternData::$store[$patternStoreKey]["header"] = $header; - PatternData::$store[$patternStoreKey]["code"] = $code; - PatternData::$store[$patternStoreKey]["footer"] = $footer; - - } - - } - - } - -} diff --git a/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php b/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php deleted file mode 100644 index aa82d1479..000000000 --- a/core/lib/PatternLab/PatternData/Helpers/PatternStateHelper.php +++ /dev/null @@ -1,123 +0,0 @@ - $patternStoreData) { - - if ($patternStoreData["category"] == "pattern") { - - $patternState = $patternStoreData["state"]; - - // make sure the pattern has a given state - if ($patternState != "") { - - $patternStateDigit = array_search($patternState, Config::$options["patternStates"]); - - // if this is a true pattern state update various patterns - if ($patternStateDigit !== false) { - - foreach (PatternData::$store as $patternStoreKey2 => $patternStoreData2) { - - if (($patternStoreData2["category"] == "pattern") && isset($patternStoreData2["lineagesR"])) { - - foreach ($patternStoreData2["lineagesR"] as $patternCheckInfo) { - - $lineagePatternPartial = $patternCheckInfo["lineagePattern"]; - - // if the found pattern's lineage is empty and the pattern state isn't the last (e.g. complete) add the pattern state - // otherwise, if the pattern state is less than the one being checked update the pattern - if ((PatternData::$store[$lineagePatternPartial]["state"] == "") && ($patternStateDigit != $patternStateLast)) { - - PatternData::$store[$lineagePatternPartial]["state"] = $patternState; - - } else { - - $patternStateCheck = array_search(PatternData::$store[$lineagePatternPartial]["state"], Config::$options["patternStates"]); - if ($patternStateDigit < $patternStateCheck) { - PatternData::$store[$lineagePatternPartial]["state"] = $patternState; - } - - } - - } - - } - - } - - } - - } - - } - - } - - // make sure we update the lineages with the pattern state if appropriate - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if ($patternStoreData["category"] == "pattern") { - - if (isset($patternStoreData["lineages"])) { - - foreach ($patternStoreData["lineages"] as $patternLineageKey => $patternLineageInfo) { - - $lineagePattern = $patternLineageInfo["lineagePattern"]; - $patternState = PatternData::$store[$lineagePattern]["state"]; - if (($patternState != "") && ($patternState != null)) { - PatternData::$store[$patternStoreKey]["lineages"][$patternLineageKey]["lineageState"] = $patternState; - } - - } - - } - - if (isset($patternStoreData["lineagesR"])) { - - foreach ($patternStoreData["lineagesR"] as $patternLineageKey => $patternLineageInfo) { - - $lineagePattern = $patternLineageInfo["lineagePattern"]; - $patternState = PatternData::$store[$lineagePattern]["state"]; - if (($patternState != "") && ($patternState != null)) { - PatternData::$store[$patternStoreKey]["lineages"][$patternLineageKey]["lineageState"] = $patternState; - } - - } - - } - - } - - } - - } - -} diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php deleted file mode 100644 index 57b6cb031..000000000 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/CSSRuleSaverHelperPlugin.php +++ /dev/null @@ -1,8 +0,0 @@ -// set-up the mark-up for CSS Rule Saver so it can figure out which rules to save -$patternCSSExists = $this->enableCSS; -$patternCSS = ""; -if ($this->enableCSS) { - $this->cssRuleSaver->loadHTML($patternCodeRaw,false); - $patternCSS = $this->cssRuleSaver->saveRules(); - $this->patternCSS[$patternSubtypeItem["patternPartial"]] = $patternCSS; -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php b/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php deleted file mode 100644 index b6aab50bf..000000000 --- a/core/lib/PatternLab/PatternData/Helpers/Plugins/KSSHelperPlugin.php +++ /dev/null @@ -1,99 +0,0 @@ -patternPaths = $options["patternPaths"]; - - } - - public function run() { - - $options = array(); - $options["patternPaths"] = $this->patternPaths; - PatternEngine::setup($options); - - $kss = KSS::parse(Config::$options["sourceDir"]); - - foreach (PatternData::$store as $patternStoreKey => $patternStoreData) { - - if ($patternStoreData["category"] == "pattern") { - - if ($kssSection = $kss->getSection($patternStoreKey)) { - - PatternData::$store[$patternStoreKey]["name"] = $kssSection->getTitle(); - PatternData::$store[$patternStoreKey]["desc"] = $kssSection->getDescription(); - PatternData::$store[$patternStoreKey]["descExists"] = true; - $modifiers = $kssSection->getModifiers(); - - if (!empty($modifiers)) { - - PatternData::$store[$patternStoreKey]["modifiersExist"] = true; - $patternModifiers = array(); - - foreach ($modifiers as $modifier) { - - $name = $modifier->getName(); - $class = $modifier->getClassName(); - $desc = $modifier->getDescription(); - $code = ""; - $modifierCodeExists = false; - - if ($name[0] != ":") { - - $data = Data::getPatternSpecificData($patternStoreKey); - $data = array_merge($data,array("styleModifier" => $class)); - - $srcPath = (isset($patternStoreData["pseudo"])) ? PatternData::$store[$patternStoreData["original"]]["pathName"] : $patternStoreData["pathName"]; - $code = Render::Pattern($srcPath,$data); - - $modifierCodeExists = true; - - } - - $patternModifiers[] = array("modifierName" => $name, - "modifierDesc" => $desc, - "modifierCode" => $code, - "modifierCodeExists" => $modifierCodeExists); - } - - PatternData::$store[$patternStoreKey]["modifiers"] = $patternModifiers; - - } - - } - - } - - } - - unset($patternLoader); - unset($patternLoaderInstance); - unset($kss); - - } - -} - diff --git a/core/lib/PatternLab/PatternData/Rule.php b/core/lib/PatternLab/PatternData/Rule.php deleted file mode 100644 index bfd39b0a5..000000000 --- a/core/lib/PatternLab/PatternData/Rule.php +++ /dev/null @@ -1,108 +0,0 @@ -depthProp != 3) && ($depth != $this->depthProp)) { - return false; - } - - if (($this->compareProp($ext,$this->extProp)) && ($isDir == $this->isDirProp) && ($isFile == $this->isFileProp)) { - $result = true; - if ($this->searchProp != "") { - $result = $this->compareProp($name,$this->searchProp); - } - if ($this->ignoreProp != "") { - $result = ($this->compareProp($name,$this->ignoreProp)) ? false : true; - } - return $result; - } - - return false; - - } - - /** - * Compare the search and ignore props against the name. - * Can use && or || in the comparison - * @param {String} the name of the item - * @param {String} the value of the property to compare - * - * @return {Boolean} whether the compare was successful or not - */ - protected function compareProp($name,$propCompare) { - - if (($name == "") && ($propCompare == "")) { - $result = true; - } else if ((($name == "") && ($propCompare != "")) || (($name != "") && ($propCompare == ""))) { - $result = false; - } else if (strpos($propCompare,"&&") !== false) { - $result = true; - $props = explode("&&",$propCompare); - foreach ($props as $prop) { - $pos = (strpos($name,$prop) !== false) ? true : false; - $result = ($result && $pos); - } - } else if (strpos($propCompare,"||") !== false) { - $result = false; - $props = explode("||",$propCompare); - foreach ($props as $prop) { - $pos = (strpos($name,$prop) !== false) ? true : false; - $result = ($result || $pos); - } - } else { - $result = (strpos($name,$propCompare) !== false) ? true : false; - } - - return $result; - - } - - /** - * Get the name for a given pattern sans any possible digits used for reordering - * @param {String} the pattern based on the filesystem name - * @param {Boolean} whether or not to strip slashes from the pattern name - * - * @return {String} a lower-cased version of the pattern name - */ - protected function getPatternName($pattern, $clean = true) { - $patternBits = explode("-",$pattern,2); - $patternName = (((int)$patternBits[0] != 0) || ($patternBits[0] == '00')) ? $patternBits[1] : $pattern; - return ($clean) ? (str_replace("-"," ",$patternName)) : $patternName; - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php b/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php deleted file mode 100644 index 43feba001..000000000 --- a/core/lib/PatternLab/PatternData/Rules/DocumentationRule.php +++ /dev/null @@ -1,94 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "md"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ""; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternType = PatternData::$patternType; - $patternTypeDash = PatternData::$patternTypeDash; - $dirSep = PatternData::$dirSep; - - // set-up the names - $docFull = $name; // 00-colors.md - $doc = str_replace(".".$this->extProp,"",$docFull); // 00-colors - $docDash = $this->getPatternName(str_replace("_","",$doc),false); // colors - $docPartial = $patternTypeDash."-".$docDash; - - // make sure the pattern isn't hidden - $hidden = ($docFull[0] == "_"); - - // if the pattern isn't hidden do stuff - if (!$hidden) { - - // parse data - $text = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); - list($yaml,$markdown) = Documentation::parse($text); - - // grab the title and unset it from the yaml so it doesn't get duped in the meta - if (isset($yaml["title"])) { - $title = $yaml["title"]; - unset($yaml["title"]); - } - - // figure out if this is a pattern subtype - $patternSubtypeDoc = false; - if ($depth == 1) { - // go through all of the directories to see if this one matches our doc - foreach (glob(__DIR__."/../..".Config::$options["patternSourceDir"].$patternType."/*",GLOB_ONLYDIR) as $dir) { - $dir = str_replace(__DIR__."/../..".Config::$options["patternSourceDir"].$patternType."/","",$dir); - if ($dir == $doc) { - $patternSubtypeDoc = true; - break; - } - } - - } - - $category = ($patternSubtypeDoc) ? "patternSubtype" : "pattern"; - $patternStoreKey = ($patternSubtypeDoc) ? $docPartial."-plsubtype" : $docPartial; - - $patternStoreData = array("category" => $category, - "nameClean" => $title, - "desc" => $markdown, - "descExists" => true, - "meta" => $yaml, - "full" => $doc); - - // if the pattern data store already exists make sure this data overwrites it - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; - - } - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php b/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php deleted file mode 100644 index 631324605..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PatternInfoListItemsRule.php +++ /dev/null @@ -1,68 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "json||yaml"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ".listitems."; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternTypeDash = PatternData::$patternTypeDash; - - // set-up the names - $patternFull = $name; // foo.listitems.json - $pattern = str_replace(".listitems.".$ext,"",$patternFull); // foo - $patternDash = $this->getPatternName($pattern,false); // foo - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo - - // should this pattern get rendered? - $hidden = ($patternFull[0] == "_"); - - if (!$hidden) { - - $patternStoreData = array("category" => "pattern"); - - $data = Data::getListItems($pathName,$ext); - $patternStoreData["listItems"] = $data; - - } - - // create a key for the data store - $patternStoreKey = $patternPartial; - - // if the pattern data store already exists make sure it is merged and overwrites this data - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; - - } - -} - diff --git a/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php b/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php deleted file mode 100644 index aec70b2d7..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PatternInfoRule.php +++ /dev/null @@ -1,77 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "json||yaml"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ""; - $this->ignoreProp = "~"; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternTypeDash = PatternData::$patternTypeDash; - - // set-up the names - $patternFull = $name; // foo.json - $pattern = str_replace(".".$ext,"",$patternFull); // foo - $patternDash = $this->getPatternName($pattern,false); // foo - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-foo - - // should this pattern get rendered? - $hidden = ($patternFull[0] == "_"); - - if (!$hidden) { - - $patternStoreData = array("category" => "pattern"); - - $file = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); - - if ($ext == "json") { - $data = json_decode($file,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($name,$jsonErrorMessage,$data); - } - } else { - $data = Yaml::parse($file); - } - - $patternStoreData["data"] = $data; - - // create a key for the data store - $patternStoreKey = $patternPartial; - - // if the pattern data store already exists make sure it is merged and overwrites this data - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive(PatternData::$store[$patternStoreKey],$patternStoreData) : $patternStoreData; - - } - - } - -} - diff --git a/core/lib/PatternLab/PatternData/Rules/PatternRule.php b/core/lib/PatternLab/PatternData/Rules/PatternRule.php deleted file mode 100644 index e225ff199..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PatternRule.php +++ /dev/null @@ -1,100 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = Config::$options["patternExtension"]; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = ""; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternSubtype = PatternData::$patternSubtype; - $patternSubtypeClean = PatternData::$patternSubtypeClean; - $patternSubtypeDash = PatternData::$patternSubtypeDash; - $patternType = PatternData::$patternType; - $patternTypeClean = PatternData::$patternTypeClean; - $patternTypeDash = PatternData::$patternTypeDash; - $dirSep = PatternData::$dirSep; - - // set-up the names - $patternFull = $name; // 00-colors.mustache - $pattern = str_replace(".".Config::$options["patternExtension"],"",$patternFull); // 00-colors - $patternState = ""; - - // check for pattern state - if (strpos($pattern,"@") !== false) { - $patternBits = explode("@",$pattern,2); - $pattern = $patternBits[0]; - $patternState = $patternBits[1]; - } - - // finish setting up vars - $patternDash = $this->getPatternName(str_replace("_","",$pattern),false); // colors - $patternClean = str_replace("-"," ",$patternDash); // colors (dashes replaced with spaces) - $patternPartial = $patternTypeDash."-".$patternDash; // atoms-colors - $patternPath = str_replace(".".Config::$options["patternExtension"],"",$pathName); // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - - // should this pattern get rendered? - $hidden = ($patternFull[0] == "_"); - - // create a key for the data store - $patternStoreKey = $patternPartial; - - // collect the data - $patternStoreData = array("category" => "pattern", - "name" => $pattern, - "partial" => $patternPartial, - "nameDash" => $patternDash, - "nameClean" => $patternClean, - "type" => $patternType, - "typeDash" => $patternTypeDash, - "breadcrumb" => $patternTypeClean, - "state" => $patternState, - "hidden" => $hidden, - "depth" => $depth, - "ext" => $ext, - "path" => $path, - "pathName" => $patternPath, - "pathDash" => $patternPathDash, - "isDir" => $this->isDirProp, - "isFile" => $this->isFileProp); - - // add any subtype info if necessary - if ($depth == 2) { - $patternStoreData["subtype"] = $patternSubtype; - $patternStoreData["subtypeDash"] = $patternSubtypeDash; - $patternStoreData["breadcrumb"] = $patternTypeClean." > ".$patternSubtypeClean; - } - - // if the pattern data store already exists make sure it is merged and overwrites this data - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php b/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php deleted file mode 100644 index 5ae93614f..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PatternSubtypeRule.php +++ /dev/null @@ -1,77 +0,0 @@ -depthProp = 1; - $this->extProp = ""; - $this->isDirProp = true; - $this->isFileProp = false; - $this->searchProp = ""; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternType = PatternData::$patternType; - $patternTypeDash = PatternData::$patternTypeDash; - $patternTypeClean = PatternData::$patternTypeClean; - $dirSep = PatternData::$dirSep; - - // set-up the names - $patternSubtype = $name; // 02-blocks - $patternSubtypeDash = $this->getPatternName($name,false); // blocks - $patternSubtypeClean = str_replace("-"," ",$patternSubtypeDash); // blocks (dashes replaced with spaces) - $patternSubtypePath = $pathName; // 00-atoms/02-blocks - $patternSubtypePathDash = str_replace($dirSep,"-",$patternSubtypePath); // 00-atoms-02-blocks (file path) - - // create a key for the data store - $patternStoreKey = $patternTypeDash."-".$patternSubtypeDash."-plsubtype"; - - // collect the data - $patternStoreData = array("category" => "patternSubtype", - "name" => $patternSubtype, - "nameDash" => $patternSubtypeDash, - "nameClean" => $patternSubtypeClean, - "type" => $patternType, - "typeDash" => $patternTypeDash, - "breadcrumb" => $patternTypeClean, - "depth" => $depth, - "ext" => $ext, - "path" => $path, - "pathName" => $patternSubtypePath, - "pathDash" => $patternSubtypePathDash, - "isDir" => $this->isDirProp, - "isFile" => $this->isFileProp); - - // if the pattern data store already exists make sure it is merged and overwrites this data - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; - - // starting a new set of pattern types. it might not have any pattern subtypes - PatternData::$patternSubtype = $patternSubtype; - PatternData::$patternSubtypeClean = $patternSubtypeClean; - PatternData::$patternSubtypeDash = $patternSubtypeDash; - PatternData::$patternSubtypeSet = true; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php b/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php deleted file mode 100644 index 240f25649..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PatternTypeRule.php +++ /dev/null @@ -1,68 +0,0 @@ -depthProp = 0; - $this->extProp = ""; - $this->isDirProp = true; - $this->isFileProp = false; - $this->searchProp = ""; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $dirSep = PatternData::$dirSep; - - // set-up the names - $patternType = $name; // 00-atoms - $patternTypeDash = $this->getPatternName($name,false); // atoms - $patternTypeClean = str_replace("-"," ",$patternTypeDash); // atoms (dashes replaced with spaces) - - $patternTypePath = $pathName; // 00-atoms/02-blocks - $patternTypePathDash = str_replace($dirSep,"-",$patternTypePath); // 00-atoms-02-blocks (file path) - - // create a key for the data store - $patternStoreKey = $patternTypeDash."-pltype"; - - // add a new patternType to the nav - PatternData::$store[$patternStoreKey] = array("category" => "patternType", - "name" => $patternType, - "nameDash" => $patternTypeDash, - "nameClean" => $patternTypeClean, - "depth" => $depth, - "ext" => $ext, - "path" => $path, - "pathName" => $patternTypePath, - "pathDash" => $patternTypePathDash, - "isDir" => $this->isDirProp, - "isFile" => $this->isFileProp); - - // starting a new set of pattern types. it might not have any pattern subtypes - PatternData::$patternType = $patternType; - PatternData::$patternTypeClean = $patternTypeClean; - PatternData::$patternTypeDash = $patternTypeDash; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php b/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php deleted file mode 100644 index 73ab58b91..000000000 --- a/core/lib/PatternLab/PatternData/Rules/PseudoPatternRule.php +++ /dev/null @@ -1,143 +0,0 @@ -depthProp = 3; // 3 means that depth won't be checked - $this->extProp = "json||yaml"; - $this->isDirProp = false; - $this->isFileProp = true; - $this->searchProp = "~"; - $this->ignoreProp = ""; - - } - - public function run($depth, $ext, $path, $pathName, $name) { - - // load default vars - $patternSubtype = PatternData::$patternSubtype; - $patternSubtypeDash = PatternData::$patternSubtypeDash; - $patternType = PatternData::$patternType; - $patternTypeDash = PatternData::$patternTypeDash; - $dirSep = PatternData::$dirSep; - - // set-up the names - $patternFull = $name; // 00-colors.mustache - $patternState = ""; - - // check for pattern state - if (strpos($patternFull,"@") !== false) { - $patternBits = explode("@",$patternFull,2); - $patternState = str_replace(".".$ext,"",$patternBits[1]); - $patternFull = preg_replace("/@(.*?)\./",".",$patternFull); - } - - // finish setting up vars - $patternBits = explode("~",$patternFull); - $patternBase = $patternBits[0].".".Config::$options["patternExtension"]; // 00-homepage.mustache - $patternBaseDash = $this->getPatternName($patternBits[0],false); // homepage - $patternBaseOrig = $patternTypeDash."-".$patternBaseDash; // pages-homepage - $patternBaseData = $patternBits[0].".".$ext; // 00-homepage.json - $stripJSON = str_replace(".".$ext,"",$patternBits[1]); - $patternBitClean = preg_replace("/@(.*?)/","",$patternBits[0]); - $pattern = $patternBitClean."-".$stripJSON; // 00-homepage-00-emergency - $patternInt = $patternBitClean."-".$this->getPatternName($stripJSON, false); // 00-homepage-emergency - $patternDash = $this->getPatternName($patternInt,false); // homepage-emergency - $patternClean = str_replace("-"," ",$patternDash); // homepage emergency - $patternPartial = $patternTypeDash."-".$patternDash; // pages-homepage-emergency - $patternPath = str_replace(".".$ext,"",str_replace("~","-",$pathName)); // 00-atoms/01-global/00-colors - $patternPathDash = str_replace($dirSep,"-",$patternPath); // 00-atoms-01-global-00-colors (file path) - $patternPathOrigBits = explode("~",$pathName); - $patternPathOrig = $patternPathOrigBits[0]; // 04-pages/00-homepage - $patternPathOrigDash = str_replace($dirSep,"-",$patternPathOrig); // 04-pages-00-homepage - - // should this pattern get rendered? - $hidden = ($patternFull[0] == "_"); - - // create a key for the data store - $patternStoreKey = $patternPartial; - - // collect the data - $patternStoreData = array("category" => "pattern", - "name" => $pattern, - "partial" => $patternPartial, - "nameDash" => $patternDash, - "nameClean" => $patternClean, - "type" => $patternType, - "typeDash" => $patternTypeDash, - "breadcrumb" => $patternType, - "state" => $patternState, - "hidden" => $hidden, - "depth" => $depth, - "ext" => $ext, - "path" => $path, - "pathName" => $patternPath, - "pathDash" => $patternPathDash, - "isDir" => $this->isDirProp, - "isFile" => $this->isFileProp, - "pseudo" => true, - "original" => $patternBaseOrig, - "pathOrig" => $patternPathOrig, - "pathOrigDash" => $patternPathOrigDash); - - // add any subtype info if necessary - if ($depth == 2) { - $patternStoreData["subtype"] = $patternSubtype; - $patternStoreData["subtypeDash"] = $patternSubtypeDash; - $patternStoreData["breadcrumb"] = $patternType." > ".$patternSubtype; - } - - $patternDataBase = array(); - if (file_exists(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$path."/".$patternBaseData)) { - $data = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$path."/".$patternBaseData); - if ($ext == "json") { - $patternDataBase = json_decode($data,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($patternBaseJSON,$jsonErrorMessage,$data); - } - } else { - $patternDataBase = Yaml::parse($data); - } - - } - - // get the data for the pseudo-pattern - $data = file_get_contents(__DIR__."/../..".Config::$options["patternSourceDir"]."/".$pathName); - if ($ext == "json") { - $patternData = json_decode($data,true); - if ($jsonErrorMessage = JSON::hasError()) { - JSON::lastErrorMsg($name,$jsonErrorMessage,$data); - } - } else { - $patternData = Yaml::parse($data); - } - - $patternStoreData["data"] = array_replace_recursive($patternDataBase, $patternData); - - // if the pattern data store already exists make sure it is merged and overwrites this data - PatternData::$store[$patternStoreKey] = isset(PatternData::$store[$patternStoreKey]) ? array_replace_recursive($patternStoreData,PatternData::$store[$patternStoreKey]) : $patternStoreData; - - } - -} - diff --git a/core/lib/PatternLab/PatternEngine.php b/core/lib/PatternLab/PatternEngine.php deleted file mode 100644 index 815f9f176..000000000 --- a/core/lib/PatternLab/PatternEngine.php +++ /dev/null @@ -1,55 +0,0 @@ -test()) { - $found = true; - self::$patternLoader = $rule->getInstance($options); - } - } - - if (!$found) { - print "the supplied pattern extension didn't match a pattern loader rule. please check.\n"; - exit; - } - - } - - /** - * Load all of the rules related to Pattern Engine - */ - public static function loadRules($options) { - - foreach (glob(__DIR__."/PatternEngine/Rules/*.php") as $filename) { - $rule = str_replace(".php","",str_replace(__DIR__."/PatternEngine/Rules/","",$filename)); - $ruleClass = "\PatternLab\PatternEngine\Rules\\".$rule; - self::$rules[] = new $ruleClass($options); - } - - } - -} diff --git a/core/lib/PatternLab/PatternEngine/Loader.php b/core/lib/PatternLab/PatternEngine/Loader.php deleted file mode 100644 index cb2986b66..000000000 --- a/core/lib/PatternLab/PatternEngine/Loader.php +++ /dev/null @@ -1,317 +0,0 @@ -patternPaths = $options["patternPaths"]; - - } - - /** - * Helper function to find and replace the given parameters in a particular partial before handing it back to Mustache - * @param {String} the file contents - * @param {Array} an array of paramters to match - * - * @return {String} the modified file contents - */ - public function findReplaceParameters($fileData, $parameters) { - $numbers = array("zero","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve"); - foreach ($parameters as $k => $v) { - if (is_array($v)) { - if (preg_match('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k.'[\s]*)}}/s',$fileData,$matches)) { - if (isset($matches[2])) { - $partialData = ""; - foreach ($v as $v2) { - $partialData .= $this->findReplaceParameters($matches[2], $v2); - } - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s',$partialData,$fileData); - } - } - } else if ($v == "true") { - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else if ($v == "false") { - $fileData = preg_replace('/{{\^([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','$2',$fileData); // {{# asdf }}STUFF{{/ asdf}} - $fileData = preg_replace('/{{\#([\s]*'.$k.'[\s]*)}}(.*?){{\/([\s]*'.$k .'[\s]*)}}/s','',$fileData); // {{^ asdf }}STUFF{{/ asdf}} - } else if ($k == "listItems") { - $v = ((int)$v != 0) && ((int)$v < 13) ? $numbers[$v] : $v; - if (($v != "zero") && in_array($v,$numbers)) { - $fileData = preg_replace('/{{\#([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{# listItems.'.$v.' }}',$fileData); - $fileData = preg_replace('/{{\/([\s]*listItems\.[A-z]{3,10}[\s]*)}}/s','{{/ listItems.'.$v.' }}',$fileData); - } - } else { - $fileData = preg_replace('/{{{([\s]*'.$k.'[\s]*)}}}/', $v, $fileData); // {{{ asdf }}} - $fileData = preg_replace('/{{([\s]*'.$k.'[\s]*)}}/', htmlspecialchars($v), $fileData); // escaped {{ asdf }} - } - } - return $fileData; - } - - /** - * Helper function for getting a Mustache template file name. - * @param {String} the pattern type for the pattern - * @param {String} the pattern sub-type - * - * @return {Array} an array of rendered partials that match the given path - */ - public function getFileName($name,$ext) { - - $fileName = ""; - $dirSep = DIRECTORY_SEPARATOR; - - // test to see what kind of path was supplied - $posDash = strpos($name,"-"); - $posSlash = strpos($name,$dirSep); - - if (($posSlash === false) && ($posDash !== false)) { - $fileName = $this->getPatternFileName($name); - } else { - $fileName = $name; - } - - if (substr($fileName, 0 - strlen($ext)) !== $ext) { - $fileName .= $ext; - } - - return $fileName; - - } - - /** - * Helper function to return the pattern file name - * @param {String} the name of the pattern - * - * @return {String} the file path to the pattern - */ - public function getPatternFileName($name) { - - $patternFileName = ""; - - list($patternType,$pattern) = $this->getPatternInfo($name); - - // see if the pattern is an exact match for patternPaths. if not iterate over patternPaths to find a likely match - if (isset($this->patternPaths[$patternType][$pattern])) { - $patternFileName = $this->patternPaths[$patternType][$pattern]; - } else if (isset($this->patternPaths[$patternType])) { - foreach($this->patternPaths[$patternType] as $patternMatchKey=>$patternMatchValue) { - $pos = strpos($patternMatchKey,$pattern); - if ($pos !== false) { - $patternFileName = $patternMatchValue; - break; - } - } - } - - return $patternFileName; - - } - - /** - * Helper function to return the parts of a partial name - * @param {String} the name of the partial - * - * @return {Array} the pattern type and the name of the pattern - */ - public function getPatternInfo($name) { - - $patternBits = explode("-",$name); - - $i = 1; - $k = 2; - $c = count($patternBits); - $patternType = $patternBits[0]; - while (!isset($this->patternPaths[$patternType]) && ($i < $c)) { - $patternType .= "-".$patternBits[$i]; - $i++; - $k++; - } - - $patternBits = explode("-",$name,$k); - $pattern = $patternBits[count($patternBits)-1]; - - return array($patternType, $pattern); - - } - - /** - * Helper function for finding if a partial name has style modifier or parameters - * @param {String} the pattern name - * - * @return {Array} an array containing just the partial name, a style modifier, and any parameters - */ - public function getPartialInfo($partial) { - - $styleModifier = array(); - $parameters = array(); - - if (strpos($partial, "(") !== false) { - $partialBits = explode("(",$partial,2); - $partial = trim($partialBits[0]); - $parametersString = substr($partialBits[1],0,(strlen($partialBits[1]) - strlen(strrchr($partialBits[1],")")))); - $parameters = $this->parseParameters($parametersString); - } - - if (strpos($partial, ":") !== false) { - $partialBits = explode(":",$partial,2); - $partial = $partialBits[0]; - $styleModifier = $partialBits[1]; - if (strpos($styleModifier, "|") !== false) { - $styleModifierBits = explode("|",$styleModifier); - $styleModifier = join(" ",$styleModifierBits); - } - $styleModifier = array("styleModifier" => $styleModifier); - } - - return array($partial,$styleModifier,$parameters); - - } - - /** - * Helper function to parse the parameters and return them as an array - * @param {String} the parameter string - * - * @return {Array} the keys and values for the parameters - */ - private function parseParameters($string) { - - $parameters = array(); - $arrayParameters = array(); - $arrayOptions = array(); - $betweenSQuotes = false; - $betweenDQuotes = false; - $inKey = true; - $inValue = false; - $inArray = false; - $inOption = false; - $char = ""; - $buffer = ""; - $keyBuffer = ""; - $arrayKeyBuffer = ""; - $strLength = strlen($string); - - for ($i = 0; $i < $strLength; $i++) { - - $previousChar = $char; - $char = $string[$i]; - - if ($inKey && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { - // if inKey, a quote, and betweenQuotes is false ignore quote, set betweenQuotes to true and empty buffer to kill spaces - ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); - } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { - // if inKey, a quote, betweenQuotes is true, and previous character is \ add to buffer - $buffer .= $char; - } else if ($inKey && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { - // if inKey, a quote, betweenQuotes is true set betweenQuotes to false, save as key buffer, empty buffer set inKey false - $keyBuffer = $buffer; - $buffer = ""; - $inKey = false; - $betweenSQuotes = false; - $betweenDQuotes = false; - } else if ($inKey && !$betweenDQuotes && !$betweenSQuotes && ($char == ":")) { - // if inKey, a colon, betweenQuotes is false, save as key buffer, empty buffer, set inKey false set inValue true - $keyBuffer = $buffer; - $buffer = ""; - $inKey = false; - $inValue = true; - } else if ($inKey) { - // if inKey add to buffer - $buffer .= $char; - } else if (!$inKey && !$inValue && ($char == ":")) { - // if inKey is false, inValue false, and a colon set inValue true - $inValue = true; - } else if ($inValue && !$inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "[")) { - // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer - $inArray = true; - $inValue = false; - $arrayKeyBuffer = trim($keyBuffer); - } else if ($inArray && !$betweenDQuotes && !$betweenSQuotes && ($char == "]")) { - // if inValue, outside quotes, and find a bracket set inArray to true and add to array buffer - $inArray = false; - $parameters[$arrayKeyBuffer] = $arrayParameters; - $arrayParameters = array(); - } else if ($inArray && !$inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "{")) { - $inOption = true; - $inKey = true; - } else if ($inArray && $inOption && !$betweenDQuotes && !$betweenSQuotes && ($char == "}")) { - $inOption = false; - $inValue = false; - $inKey = false; - $arrayParameters[] = $arrayOptions; - $arrayOptions = array(); - } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && (($char == "\"") || ($char == "'"))) { - // if inValue, a quote, and betweenQuote is false set betweenQuotes to true and empty buffer to kill spaces - ($char == "\"") ? ($betweenDQuotes = true) : ($betweenSQuotes = true); - } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'"))) && ($previousChar == "\\")) { - // if inValue, a quote, betweenQuotes is true, and previous character is \ add to buffer - $buffer .= $char; - } else if ($inValue && (($betweenDQuotes && ($char == "\"")) || ($betweenSQuotes && ($char == "'")))) { - // if inValue, a quote, betweenQuotes is true set betweenQuotes to false, save to parameters array, empty buffer, set inValue false - $buffer = str_replace("\\\"","\"",$buffer); - $buffer = str_replace('\\\'','\'',$buffer); - if ($inArray) { - $arrayOptions[trim($keyBuffer)] = trim($buffer); - } else { - $parameters[trim($keyBuffer)] = trim($buffer); - } - $buffer = ""; - $inValue = false; - $betweenSQuotes = false; - $betweenDQuotes = false; - } else if ($inValue && !$betweenDQuotes && !$betweenSQuotes && ($char == ",")) { - // if inValue, a comman, betweenQuotes is false, save to parameters array, empty buffer, set inValue false, set inKey true - if ($inArray) { - $arrayOptions[trim($keyBuffer)] = trim($buffer); - } else { - $parameters[trim($keyBuffer)] = trim($buffer); - } - $buffer = ""; - $inValue = false; - $inKey = true; - } else if ($inValue && (($i + 1) == $strLength)) { - // if inValue and end of the string add to buffer, save to parameters array - $buffer .= $char; - if ($inArray) { - $arrayOptions[trim($keyBuffer)] = trim($buffer); - } else { - $parameters[trim($keyBuffer)] = trim($buffer); - } - } else if ($inValue) { - // if inValue add to buffer - $buffer .= $char; - } else if (!$inValue && !$inKey && ($char == ",")) { - // if inValue is false, inKey false, and a comma set inKey true - if ($inArray && !$inOption) { - // don't do anything - } else { - $inKey = true; - } - - } - } - - return $parameters; - - } - - -} diff --git a/core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php b/core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php deleted file mode 100644 index 4aea2a373..000000000 --- a/core/lib/PatternLab/PatternEngine/Loaders/MustacheLoader.php +++ /dev/null @@ -1,158 +0,0 @@ - '.ms', - * ); - * - * @throws Mustache_Exception_RuntimeException if $baseDir does not exist. - * - * @param string $baseDir Base directory containing Mustache template files. - * @param array $options Array of Loader options (default: array()) - */ - public function __construct($baseDir, array $options = array()) { - - $this->baseDir = rtrim(realpath($baseDir), '/'); - - if (!is_dir($this->baseDir)) { - throw new \Mustache_Exception_RuntimeException(sprintf('FilesystemLoader baseDir must be a directory: %s', $baseDir)); - } - - if (array_key_exists('extension', $options)) { - if (empty($options['extension'])) { - $this->extension = ''; - } else { - $this->extension = '.' . ltrim($options['extension'], '.'); - } - } - - if (array_key_exists('patternPaths', $options)) { - $this->patternPaths = $options['patternPaths']; - } - - $this->patternLoader = new \PatternLab\PatternEngine\Loader($options); - - } - - /** - * Load a Template by name. - * - * $loader = new Mustache_Loader_FilesystemLoader(dirname(__FILE__).'/views'); - * $loader->load('admin/dashboard'); // loads "./views/admin/dashboard.mustache"; - * - * @param string $name - * - * @return string Mustache Template source - */ - public function load($name) { - - if (!isset($this->templates[$name])) { - try { - $this->templates[$name] = $this->loadFile($name); - } catch (Exception $e) { - print "The partial, ".$name.", wasn't found so a pattern failed to build.\n"; - } - } - - return (isset($this->templates[$name])) ? $this->templates[$name] : false; - - } - - /** - * Helper function for loading a Mustache file by name. - * - * @throws Mustache_Exception_UnknownTemplateException If a template file is not found. - * - * @param string $name - * - * @return string Mustache Template source - */ - protected function loadFile($name) { - - // get pattern data - list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); - - // get the real file path for the pattern - $fileName = $this->baseDir."/".$this->patternLoader->getFileName($partialName,$this->extension); - - // throw error if path is not found - if (!file_exists($fileName)) { - throw new \Mustache_Exception_UnknownTemplateException($fileName); - } - - // get the file data - $fileData = file_get_contents($fileName); - - // if the pattern name had a style modifier find & replace it - if (count($styleModifier) > 0) { - $fileData = $this->patternLoader->findReplaceParameters($fileData, $styleModifier); - } - - // if the pattern name had parameters find & replace them - if (count($parameters) > 0) { - $fileData = $this->patternLoader->findReplaceParameters($fileData, $parameters); - } - - return $fileData; - - } - - /** - * Helper function for getting a Mustache template file name. - * @param {String} the pattern type for the pattern - * @param {String} the pattern sub-type - * - * @return {Array} an array of rendered partials that match the given path - */ - protected function getFileName($name) { - - // defaults - $fileName = ""; - $dirSep = DIRECTORY_SEPARATOR; - - // test to see what kind of path was supplied - $posDash = strpos($name,"-"); - $posSlash = strpos($name,$dirSep); - - if (($posSlash === false) && ($posDash !== false)) { - $fileName = $this->baseDir.$dirSep.$this->patternLoader->getPatternFileName($name); - } else { - $fileName = $this->baseDir.$dirSep.$name; - } - - if (substr($fileName, 0 - strlen($this->extension)) !== $this->extension) { - $fileName .= $this->extension; - } - - return $fileName; - - } - -} diff --git a/core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php b/core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php deleted file mode 100644 index 884e0580f..000000000 --- a/core/lib/PatternLab/PatternEngine/Loaders/TwigLoader.php +++ /dev/null @@ -1,232 +0,0 @@ - - * - */ - -namespace PatternLab\PatternEngine\Loaders; - -use \PatternLab\PatternEngine\Loader; - -class TwigLoader implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface { - - /** Identifier of the main namespace. */ - const MAIN_NAMESPACE = '__main__'; - - protected $paths = array(); - protected $cache = array(); - protected $patternPaths = array(); - protected $extension = '.twig'; - - /** - * Constructor. - * - * @param string|array $paths A path or an array of paths where to look for templates - */ - public function __construct($paths = array(),$patternPaths = array()) { - if ($paths) { - $this->setPaths($paths); - } - $this->patternPaths = $patternPaths['patternPaths']; - $this->patternLoader = new Loader($this->patternPaths); - } - - /** - * Returns the paths to the templates. - * - * @param string $namespace A path namespace - * - * @return array The array of paths where to look for templates - */ - public function getPaths($namespace = self::MAIN_NAMESPACE) { - return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array(); - } - - /** - * Returns the path namespaces. - * - * The main namespace is always defined. - * - * @return array The array of defined namespaces - */ - public function getNamespaces() { - return array_keys($this->paths); - } - - /** - * Sets the paths where templates are stored. - * - * @param string|array $paths A path or an array of paths where to look for templates - * @param string $namespace A path namespace - */ - public function setPaths($paths, $namespace = self::MAIN_NAMESPACE) { - if (!is_array($paths)) { - $paths = array($paths); - } - - $this->paths[$namespace] = array(); - foreach ($paths as $path) { - $this->addPath($path, $namespace); - } - } - - /** - * Adds a path where templates are stored. - * - * @param string $path A path where to look for templates - * @param string $namespace A path name - * - * @throws Twig_Error_Loader - */ - public function addPath($path, $namespace = self::MAIN_NAMESPACE) { - // invalidate the cache - $this->cache = array(); - - if (!is_dir($path)) { - throw new \Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); - } - - $this->paths[$namespace][] = rtrim($path, '/\\'); - } - - /** - * Prepends a path where templates are stored. - * - * @param string $path A path where to look for templates - * @param string $namespace A path name - * - * @throws Twig_Error_Loader - */ - public function prependPath($path, $namespace = self::MAIN_NAMESPACE) { - // invalidate the cache - $this->cache = array(); - - if (!is_dir($path)) { - throw new \Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); - } - - $path = rtrim($path, '/\\'); - - if (!isset($this->paths[$namespace])) { - $this->paths[$namespace][] = $path; - } else { - array_unshift($this->paths[$namespace], $path); - } - - } - - /** - * {@inheritdoc} - */ - public function getSource($name) { - return file_get_contents($this->findTemplate($name)); - } - - /** - * {@inheritdoc} - */ - public function getCacheKey($name) { - return $this->findTemplate($name); - } - - /** - * {@inheritdoc} - */ - public function exists($name) { - - $name = $this->normalizeName($name); - - if (isset($this->cache[$name])) { - return true; - } - - try { - $this->findTemplate($name); - - return true; - } catch (\Twig_Error_Loader $exception) { - return false; - } - } - - /** - * {@inheritdoc} - */ - public function isFresh($name, $time) { - return filemtime($this->findTemplate($name)) <= $time; - } - - protected function findTemplate($name) { - - list($partialName,$styleModifier,$parameters) = $this->patternLoader->getPartialInfo($name); - - $name = $this->patternLoader->getFileName($partialName,$this->extension); - - $name = $this->normalizeName($name); - - if (isset($this->cache[$name])) { - return $this->cache[$name]; - } - - $this->validateName($name); - - $namespace = self::MAIN_NAMESPACE; - $shortname = $name; - if (isset($name[0]) && '@' == $name[0]) { - if (false === $pos = strpos($name, '/')) { - throw new \Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); - } - - $namespace = substr($name, 1, $pos - 1); - $shortname = substr($name, $pos + 1); - } - - if (!isset($this->paths[$namespace])) { - throw new \Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); - } - - foreach ($this->paths[$namespace] as $path) { - if (is_file($path.'/'.$shortname)) { - return $this->cache[$name] = $path.'/'.$shortname; - } - } - - - throw new \Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]))); - } - - protected function normalizeName($name) { - return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/')); - } - - protected function validateName($name) { - - if (false !== strpos($name, "\0")) { - throw new \Twig_Error_Loader('A template name cannot contain NUL bytes.'); - } - - $name = ltrim($name, '/'); - $parts = explode('/', $name); - $level = 0; - foreach ($parts as $part) { - if ('..' === $part) { - --$level; - } elseif ('.' !== $part) { - ++$level; - } - - if ($level < 0) { - throw new \Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); - } - } - - } - -} diff --git a/core/lib/PatternLab/PatternEngine/Rule.php b/core/lib/PatternLab/PatternEngine/Rule.php deleted file mode 100644 index c063a279e..000000000 --- a/core/lib/PatternLab/PatternEngine/Rule.php +++ /dev/null @@ -1,33 +0,0 @@ -engineProp == Config::$options["patternExtension"]); - - } - -} diff --git a/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php b/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php deleted file mode 100644 index 3cd18f33a..000000000 --- a/core/lib/PatternLab/PatternEngine/Rules/MustacheRule.php +++ /dev/null @@ -1,38 +0,0 @@ -engineProp = "mustache"; - - } - - public function getInstance($options) { - - $options["loader"] = new MustacheLoader(__DIR__."/../../".Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); - $options["partial_loader"] = new MustacheLoader(__DIR__."/../../".Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); - - return new \Mustache_Engine($options); - - } - -} diff --git a/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php b/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php deleted file mode 100644 index 2e3f2c258..000000000 --- a/core/lib/PatternLab/PatternEngine/Rules/TwigRule.php +++ /dev/null @@ -1,37 +0,0 @@ -engineProp = "twig"; - - } - - public function getInstance() { - - $options = new TwigLoader(Config::$options["patternSourceDir"],array("patternPaths" => $options["patternPaths"])); - - return new \Twig_Environment($options); - - } - -} diff --git a/core/lib/PatternLab/Render.php b/core/lib/PatternLab/Render.php deleted file mode 100644 index c7140a07d..000000000 --- a/core/lib/PatternLab/Render.php +++ /dev/null @@ -1,63 +0,0 @@ -render($filePath,$data); - return $pattern; - - } - - /** - * Renders a given mark-up (header) using Mustache and incorporating the provided data - * @param {String} the mark-up to be rendered - * @param {Array} the data related to the pattern - * - * @return {String} the mark-up as rendered by Mustache - */ - public static function Header($html,$data) { - - $header = Helper::$htmlLoader->render($html,$data); - return $header; - - } - - /** - * Renders a given mark-up (footer) using Mustache and incorporating the provided data - * @param {String} the mark-up to be rendered - * @param {Array} the data related to the pattern - * - * @return {String} the mark-up as rendered by Mustache - */ - public static function Footer($html,$data) { - - $footer = Helper::$htmlLoader->render($html,$data); - return $footer; - - } - -} diff --git a/core/lib/PatternLab/Snapshot.php b/core/lib/PatternLab/Snapshot.php deleted file mode 100644 index 657520610..000000000 --- a/core/lib/PatternLab/Snapshot.php +++ /dev/null @@ -1,143 +0,0 @@ -publicDir = __DIR__."/../../../".Config::$options["sourceDir"]; - $this->sourceDir = "/../../../".Config::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR; - $this->snapshotsBase = $this->publicDir."/snapshots/"; - } - - /** - * Take a snap shot of the public dir - * @param {String} the directory where we wil be writing the snapshot - */ - public function takeSnapshot($dir) { - - $snapshotsDir = ""; - - // check to see if snapshots exists, if it doesn't make it - if (!is_dir($this->snapshotsBase)) { - mkdir($this->snapshotsBase); - } - - // see if the dir passed through exists. if it does highlight an error. - if ($dir) { - - // check to see if the given directory exists - if (is_dir($this->snapshotsBase.$dir)) { - print "The directory, ".$dir.", already exists. Please choose a new one for your snapshot.\n"; - exit; - } - - // set-up the final snapshot directory - $snapshotsDir = $this->snapshotsBase.$dir; - - } else { - - // get a list of dirs - $scannedDirs = scandir($this->snapshotsBase); - - // remove the dot files - $key = array_search('.', $scannedDirs); - unset($scannedDirs[$key]); - $key = array_search('..', $scannedDirs); - unset($scannedDirs[$key]); - - // set-up the final snapshot directory - $dirCount = 0; - foreach ($scannedDirs as $scannedDir) { - if (preg_match("/^v[0-9]{1,3}$/",$scannedDir)) { - $dirCount++; - } - } - - $dirCount = $dirCount + 1; - $snapshotsDir = $this->snapshotsBase."v".$dirCount; - - } - - // make the snapshot directory - mkdir($snapshotsDir); - - // iterate over all of the other files in the source directory - $directoryIterator = new \RecursiveDirectoryIterator($this->publicDir); - $filteredIterator = new FilterIterator($directoryIterator); - $objects = new \RecursiveIteratorIterator($filteredIterator, \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace($this->publicDir.DIRECTORY_SEPARATOR,"",$name); - - // check to see if it's a new directory - if ($object->isDir()) { - mkdir($snapshotsDir."/".$fileName); - } - - // check to see if it's a new file or a file that has changed - if ($object->isFile()) { - copy($this->publicDir."/".$fileName,$snapshotsDir."/".$fileName); - } - - } - - // re-scan to get the latest addition - $html = ""; - $scannedDirs = scandir($this->snapshotsBase); - - // remove the dot files - $key = array_search('.', $scannedDirs); - unset($scannedDirs[$key]); - $key = array_search('..', $scannedDirs); - unset($scannedDirs[$key]); - $key = array_search('index.html', $scannedDirs); - unset($scannedDirs[$key]); - - usort($scannedDirs, "strnatcmp"); - - foreach ($scannedDirs as $scanDir) { - $html .= "
  • ".$scanDir."
  • \n"; - } - - $d = array("html" => $html); - - TemplateHelper::setup(); - - // render the viewall template - $h = Helper::$htmlLoader->render(Helper::$mainPageHead); - $f = Helper::$htmlLoader->render(Helper::$mainPageFoot); - - // render the snapshot page - $r = Helper::$filesystemLoader->render("snapshot", $d); - $r = $h.$r.$f; - - file_put_contents($this->snapshotsBase."index.html",$r); - - print "finished taking a snapshot...\n"; - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/Snapshot/FilterIterator.php b/core/lib/PatternLab/Snapshot/FilterIterator.php deleted file mode 100644 index 16a100acc..000000000 --- a/core/lib/PatternLab/Snapshot/FilterIterator.php +++ /dev/null @@ -1,26 +0,0 @@ -current()->getFilename(),self::$FILTERS,true); - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/StarterKit.php b/core/lib/PatternLab/StarterKit.php deleted file mode 100644 index 568dd4e5c..000000000 --- a/core/lib/PatternLab/StarterKit.php +++ /dev/null @@ -1,147 +0,0 @@ -getStarterKitInfo($starterKit); - - //get the path to the GH repo and validate it - $tarballUrl = "https://github.com/".$org."/".$repo."/archive/".$tag.".tar.gz"; - - print "downloading the starter kit...\n"; - - // try to download the given starter kit - if (!$starterKit = @file_get_contents($tarballUrl)) { - $error = error_get_last(); - print "starter kit wasn't downloaded because:\n ".$error["message"]."\n"; - exit; - } - - // write the starter kit to the temp directory - $tempFile = tempnam(sys_get_temp_dir(), "pl-sk-archive.tar.gz"); - file_put_contents($tempFile, $starterKit); - - // see if the source directory is empty - $emptyDir = true; - $objects = new \DirectoryIterator(Config::$options["sourceDir"]); - foreach ($objects as $object) { - if (!$object->isDot() && ($object->getFilename() != "README") && ($object->getFilename() != ".DS_Store")) { - $emptyDir = false; - } - } - - print "installing the starter kit...\n"; - - // if source directory isn't empty ask if it's ok to nuke what's there - if (!$emptyDir) { - $stdin = fopen("php://stdin", "r"); - print("delete everything in source/ before installing the starter kit? Y/n\n"); - $answer = strtolower(trim(fgets($stdin))); - fclose($stdin); - if ($answer == "y") { - - print "nuking everything in source/...\n"; - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]), \RecursiveIteratorIterator::CHILD_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - if ($object->isDir()) { - rmdir($name); - } else if ($object->isFile()) { - unlink($name); - } - - } - - } else { - print "aborting install of the starter kit...\n"; - unlink($tempFile); - exit; - } - - } - - // extract - $zippy = Zippy::load(); - $zipAdapter = $zippy->getAdapterFor('tar.gz'); - $archiveZip = $zipAdapter->open($tempFile); - $archiveZip = $archiveZip->extract(Config::$options["sourceDir"]); - - // remove the temp file - unlink($tempFile); - - print "starter kit installation complete...\n"; - - } - - /** - * Break up the starterKit path - * @param {String} path of the GitHub repo - * - * @return {Array} the parts of the starter kit path - */ - protected function getStarterKitInfo($starterKit) { - - $org = ""; - $repo = ""; - $tag = "master"; - - if (strpos($starterKit, "#") !== false) { - list($starterKit,$tag) = explode("#",$starterKit); - } - - if (strpos($starterKit, "/") !== false) { - list($org,$repo) = explode("/",$starterKit); - } else { - print "please provide a real path to a starter kit...\n"; - exit; - } - - return array($org,$repo,$tag); - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/Template/Helper.php b/core/lib/PatternLab/Template/Helper.php deleted file mode 100644 index 2062e4e1b..000000000 --- a/core/lib/PatternLab/Template/Helper.php +++ /dev/null @@ -1,56 +0,0 @@ -fileSystem(); - self::$htmlLoader = $templateLoader->vanilla(); - - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/Template/Loader.php b/core/lib/PatternLab/Template/Loader.php deleted file mode 100644 index facc36e82..000000000 --- a/core/lib/PatternLab/Template/Loader.php +++ /dev/null @@ -1,38 +0,0 @@ - new \Mustache_Loader_FilesystemLoader(__DIR__."/../../../templates/"), - "partials_loader" => new \Mustache_Loader_FilesystemLoader(__DIR__."/../../../templates/partials/") - )); - } - - /** - * Load a new Mustache instance that is just a vanilla Mustache rendering engine - * - * @return {Object} an instance of the Mustache engine - */ - public static function vanilla() { - return new \Mustache_Engine; - } - -} \ No newline at end of file diff --git a/core/lib/PatternLab/Util.php b/core/lib/PatternLab/Util.php deleted file mode 100644 index 6b74856a4..000000000 --- a/core/lib/PatternLab/Util.php +++ /dev/null @@ -1,147 +0,0 @@ -setFlags(\FilesystemIterator::SKIP_DOTS); - - // for each file figure out what to do with it - foreach($objects as $name => $object) { - - if ($object->isDir()) { - // if this is a directory remove it - rmdir($name); - } else if ($object->isFile() && ($object->getFilename() != "README")) { - // if this is a file remove it - unlink($name); - } - - } - - } - - // scan source/ & public/ to figure out what directories might need to be cleaned up - $sourceDirs = glob(Config::$options["sourceDir"]."/*",GLOB_ONLYDIR); - $publicDirs = glob(Config::$options["publicDir"]."/*",GLOB_ONLYDIR); - - // make sure some directories aren't deleted - $ignoreDirs = array("styleguide","snapshots"); - foreach ($ignoreDirs as $ignoreDir) { - $key = array_search(Config::$options["publicDir"]."/".$ignoreDir,$publicDirs); - if ($key !== false){ - unset($publicDirs[$key]); - } - } - - // compare source dirs against public. remove those dirs w/ an underscore in source/ from the public/ list - foreach ($sourceDirs as $sourceDir) { - $cleanDir = str_replace(Config::$options["sourceDir"]."/","",$sourceDir); - if ($cleanDir[0] == "_") { - $key = array_search(Config::$options["publicDir"]."/".str_replace("_","",$cleanDir),$publicDirs); - if ($key !== false){ - unset($publicDirs[$key]); - } - } - } - - // for the remaining dirs in public delete them and their files - foreach ($publicDirs as $dir) { - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::CHILD_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - if ($object->isDir()) { - rmdir($name); - } else if ($object->isFile()) { - unlink($name); - } - - } - - rmdir($dir); - - } - - } - - /** - * Go through data and replace any values that match items from the link.array - * @param {String} an entry from one of the list-based config entries - * - * @return {String} trimmed version of the given $v var - */ - public static function compareReplace(&$value) { - if (is_string($value)) { - $valueCheck = strtolower($value); - $valueThin = str_replace("link.","",$valueCheck); - if ((strpos($valueCheck, 'link.') !== false) && array_key_exists($valueThin,Data::$store["link"])) { - $value = Data::$store["link"][$valueThin]; - } - } - - } - - /** - * Lowercase the given string. Used in the array_walk() function in __construct as a sanity check - * @param {String} an entry from one of the list-based config entries - * - * @return {String} lowercased version of the given $v var - */ - public static function strtolower(&$v) { - $v = strtolower($v); - } - - /** - * Trim a given string. Used in the array_walk() function in __construct as a sanity check - * @param {String} an entry from one of the list-based config entries - * - * @return {String} trimmed version of the given $v var - */ - public static function trim(&$v) { - $v = trim($v); - } - - /** - * Write out the time tracking file so the content sync service will work. A holdover - * from how I put together the original AJAX polling set-up. - */ - public static function updateChangeTime() { - - if (is_dir(Config::$options["publicDir"]."/")) { - file_put_contents(Config::$options["publicDir"]."/latest-change.txt",time()); - } else { - print "Either the public directory for Pattern Lab doesn't exist or the builder is in the wrong location. Please fix."; - exit; - } - - } - -} diff --git a/core/lib/PatternLab/Watcher.php b/core/lib/PatternLab/Watcher.php deleted file mode 100644 index 21357e48c..000000000 --- a/core/lib/PatternLab/Watcher.php +++ /dev/null @@ -1,274 +0,0 @@ -patterns = new \stdClass(); - - print "watching your site for changes...\n"; - - // run forever - while (true) { - - // clone the patterns so they can be checked in case something gets deleted - $cp = clone $o->patterns; - - // iterate over the patterns & related data and regenerate the entire site if they've changed - $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_patterns/"), \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($patternObjects as $name => $object) { - - // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace(Config::$options["sourceDir"]."/_patterns".DIRECTORY_SEPARATOR,"",$name); - $fileNameClean = str_replace(DIRECTORY_SEPARATOR."_",DIRECTORY_SEPARATOR,$fileName); - - if ($object->isFile() && (($object->getExtension() == "mustache") || ($object->getExtension() == "json") || ($object->getExtension() == "md"))) { - - // make sure this isn't a hidden pattern - $patternParts = explode(DIRECTORY_SEPARATOR,$fileName); - $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1]; - - - // make sure the pattern still exists in source just in case it's been deleted during the iteration - if (file_exists($name)) { - - $mt = $object->getMTime(); - if (isset($o->patterns->$fileName) && ($o->patterns->$fileName != $mt)) { - $o->patterns->$fileName = $mt; - $this->updateSite($fileName,"changed"); - } else if (!isset($o->patterns->$fileName) && $c) { - $o->patterns->$fileName = $mt; - $this->updateSite($fileName,"added"); - if ($object->getExtension() == "mustache") { - $patternSrcPath = str_replace(".mustache","",$fileName); - $patternDestPath = str_replace("/","-",$patternSrcPath); - $render = ($pattern[0] != "_") ? true : false; - $this->patternPaths[$patternParts[0]][$pattern] = array("patternSrcPath" => $patternSrcPath, "patternDestPath" => $patternDestPath, "render" => $render); - } - } else if (!isset($o->patterns->$fileName)) { - $o->patterns->$fileName = $mt; - } - - if ($c && isset($o->patterns->$fileName)) { - unset($cp->$fileName); - } - - } else { - - // the file was removed during the iteration so remove references to the item - unset($o->patterns->$fileName); - unset($cp->$fileName); - unset($this->patternPaths[$patternParts[0]][$pattern]); - $this->updateSite($fileName,"removed"); - - } - - } - - } - - // make sure old entries are deleted - // will throw "pattern not found" errors if an entire directory is removed at once but that shouldn't be a big deal - if ($c) { - - foreach($cp as $fileName => $mt) { - - unset($o->patterns->$fileName); - $patternParts = explode(DIRECTORY_SEPARATOR,$fileName); - $pattern = isset($patternParts[2]) ? $patternParts[2] : $patternParts[1]; - - unset($this->patternPaths[$patternParts[0]][$pattern]); - $this->updateSite($fileName,"removed"); - - } - - } - - // iterate over the data files and regenerate the entire site if they've changed - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/_annotations/"), \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - $fileName = str_replace(Config::$options["sourceDir"]."/_annotations".DIRECTORY_SEPARATOR,"",$name); - $mt = $object->getMTime(); - - if (!isset($o->$fileName)) { - $o->$fileName = $mt; - $this->updateSite($fileName,"added"); - } else if ($o->$fileName != $mt) { - $o->$fileName = $mt; - $this->updateSite($fileName,"changed"); - } - - } - - // iterate over all of the other files in the source directory and move them if their modified time has changed - if ($moveStatic) { - - $objects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::$options["sourceDir"]."/"), \RecursiveIteratorIterator::SELF_FIRST); - - // make sure dots are skipped - $objects->setFlags(\FilesystemIterator::SKIP_DOTS); - - foreach($objects as $name => $object) { - - // clean-up the file name and make sure it's not one of the pattern lab files or to be ignored - $fileName = str_replace(Config::$options["sourceDir"].DIRECTORY_SEPARATOR,"",$name); - if (($fileName[0] != "_") && (!in_array($object->getExtension(),$this->ie)) && (!in_array($object->getFilename(),$this->id))) { - - // catch directories that have the ignored dir in their path - $ignoreDir = $this->ignoreDir($fileName); - - // check to see if it's a new directory - if (!$ignoreDir && $object->isDir() && !isset($o->$fileName) && !is_dir(Config::$options["publicDir"]."/".$fileName)) { - mkdir(Config::$options["publicDir"]."/".$fileName); - $o->$fileName = "dir created"; // placeholder - print $fileName."/ directory was created...\n"; - } - - // check to see if it's a new file or a file that has changed - if (file_exists($name)) { - - $mt = $object->getMTime(); - if (!$ignoreDir && $object->isFile() && !isset($o->$fileName) && !file_exists(Config::$options["publicDir"]."/".$fileName)) { - $o->$fileName = $mt; - FileUtil::moveStaticFile($fileName,"added"); - if ($object->getExtension() == "css") { - $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons - } - } else if (!$ignoreDir && $object->isFile() && isset($o->$fileName) && ($o->$fileName != $mt)) { - $o->$fileName = $mt; - FileUtil::moveStaticFile($fileName,"changed"); - if ($object->getExtension() == "css") { - $this->updateSite($fileName,"changed",0); // make sure the site is updated for MQ reasons - } - } else if (!isset($o->fileName)) { - $o->$fileName = $mt; - } - - } else { - unset($o->$fileName); - } - - } - - } - - } - - - $c = true; - - // taking out the garbage. basically killing mustache after each run. - if (gc_enabled()) gc_collect_cycles(); - - // output anything the reload server might send our way - if ($reload) { - $output = fgets($fp, 100); - if ($output != "\n") print $output; - } - - // pause for .05 seconds to give the CPU a rest - usleep(50000); - - } - - // close the auto-reload process, this shouldn't do anything - fclose($fp); - - } - - /** - * Updates the Pattern Lab Website and prints the appropriate message - * @param {String} file name to included in the message - * @param {String} a switch for decided which message isn't printed - * - * @return {String} the final message - */ - private function updateSite($fileName,$message,$verbose = true) { - - Data::gather(); - PatternData::gather(); - - $this->generateIndex(); - $this->generateStyleguide(); - $this->generateViewAllPages(); - $this->generatePatterns(); - $this->generateAnnotations(); - - Util::updateChangeTime(); - - if ($verbose) { - if ($message == "added") { - print $fileName." was added to Pattern Lab. Reload the website to see this change in the navigation...\n"; - } elseif ($message == "removed") { - print $fileName." was removed from Pattern Lab. Reload the website to see this change reflected in the navigation...\n"; - } elseif ($message == "hidden") { - print $fileName." was hidden from Pattern Lab. Reload the website to see this change reflected in the navigation...\n"; - } else { - print $fileName." changed...\n"; - } - } - } - -} diff --git a/core/lib/Seld/JsonLint/JsonParser.php b/core/lib/Seld/JsonLint/JsonParser.php deleted file mode 100755 index d94d6bf49..000000000 --- a/core/lib/Seld/JsonLint/JsonParser.php +++ /dev/null @@ -1,464 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Seld\JsonLint; - -use stdClass; - -/** - * Parser class - * - * Example: - * - * $parser = new JsonParser(); - * // returns null if it's valid json, or an error object - * $parser->lint($json); - * // returns parsed json, like json_decode does, but slower, throws exceptions on failure. - * $parser->parse($json); - * - * Ported from https://github.com/zaach/jsonlint - */ -class JsonParser -{ - const DETECT_KEY_CONFLICTS = 1; - const ALLOW_DUPLICATE_KEYS = 2; - - private $flags; - private $stack; - private $vstack; // semantic value stack - private $lstack; // location stack - - private $yy; - private $symbols = array( - 'error' => 2, - 'JSONString' => 3, - 'STRING' => 4, - 'JSONNumber' => 5, - 'NUMBER' => 6, - 'JSONNullLiteral' => 7, - 'NULL' => 8, - 'JSONBooleanLiteral' => 9, - 'TRUE' => 10, - 'FALSE' => 11, - 'JSONText' => 12, - 'JSONValue' => 13, - 'EOF' => 14, - 'JSONObject' => 15, - 'JSONArray' => 16, - '{' => 17, - '}' => 18, - 'JSONMemberList' => 19, - 'JSONMember' => 20, - ':' => 21, - ',' => 22, - '[' => 23, - ']' => 24, - 'JSONElementList' => 25, - '$accept' => 0, - '$end' => 1, - ); - - private $terminals_ = array( - 2 => "error", - 4 => "STRING", - 6 => "NUMBER", - 8 => "NULL", - 10 => "TRUE", - 11 => "FALSE", - 14 => "EOF", - 17 => "{", - 18 => "}", - 21 => ":", - 22 => ",", - 23 => "[", - 24 => "]", - ); - - private $productions_ = array( - 0, - array(3, 1), - array(5, 1), - array(7, 1), - array(9, 1), - array(9, 1), - array(12, 2), - array(13, 1), - array(13, 1), - array(13, 1), - array(13, 1), - array(13, 1), - array(13, 1), - array(15, 2), - array(15, 3), - array(20, 3), - array(19, 1), - array(19, 3), - array(16, 2), - array(16, 3), - array(25, 1), - array(25, 3) - ); - - private $table = array(array(3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 12 => 1, 13 => 2, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 1 => array(3)), array( 14 => array(1,16)), array( 14 => array(2,7), 18 => array(2,7), 22 => array(2,7), 24 => array(2,7)), array( 14 => array(2,8), 18 => array(2,8), 22 => array(2,8), 24 => array(2,8)), array( 14 => array(2,9), 18 => array(2,9), 22 => array(2,9), 24 => array(2,9)), array( 14 => array(2,10), 18 => array(2,10), 22 => array(2,10), 24 => array(2,10)), array( 14 => array(2,11), 18 => array(2,11), 22 => array(2,11), 24 => array(2,11)), array( 14 => array(2,12), 18 => array(2,12), 22 => array(2,12), 24 => array(2,12)), array( 14 => array(2,3), 18 => array(2,3), 22 => array(2,3), 24 => array(2,3)), array( 14 => array(2,4), 18 => array(2,4), 22 => array(2,4), 24 => array(2,4)), array( 14 => array(2,5), 18 => array(2,5), 22 => array(2,5), 24 => array(2,5)), array( 14 => array(2,1), 18 => array(2,1), 21 => array(2,1), 22 => array(2,1), 24 => array(2,1)), array( 14 => array(2,2), 18 => array(2,2), 22 => array(2,2), 24 => array(2,2)), array( 3 => 20, 4 => array(1,12), 18 => array(1,17), 19 => 18, 20 => 19 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 23, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15), 24 => array(1,21), 25 => 22 ), array( 1 => array(2,6)), array( 14 => array(2,13), 18 => array(2,13), 22 => array(2,13), 24 => array(2,13)), array( 18 => array(1,24), 22 => array(1,25)), array( 18 => array(2,16), 22 => array(2,16)), array( 21 => array(1,26)), array( 14 => array(2,18), 18 => array(2,18), 22 => array(2,18), 24 => array(2,18)), array( 22 => array(1,28), 24 => array(1,27)), array( 22 => array(2,20), 24 => array(2,20)), array( 14 => array(2,14), 18 => array(2,14), 22 => array(2,14), 24 => array(2,14)), array( 3 => 20, 4 => array(1,12), 20 => 29 ), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 30, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 14 => array(2,19), 18 => array(2,19), 22 => array(2,19), 24 => array(2,19)), array( 3 => 5, 4 => array(1,12), 5 => 6, 6 => array(1,13), 7 => 3, 8 => array(1,9), 9 => 4, 10 => array(1,10), 11 => array(1,11), 13 => 31, 15 => 7, 16 => 8, 17 => array(1,14), 23 => array(1,15)), array( 18 => array(2,17), 22 => array(2,17)), array( 18 => array(2,15), 22 => array(2,15)), array( 22 => array(2,21), 24 => array(2,21)), - ); - - private $defaultActions = array( - 16 => array(2, 6) - ); - - /** - * @param string $input JSON string - * @return null|ParsingException null if no error is found, a ParsingException containing all details otherwise - */ - public function lint($input) - { - try { - $this->parse($input); - } catch (ParsingException $e) { - return $e; - } - } - - /** - * @param string $input JSON string - * @return mixed - * @throws ParsingException - */ - public function parse($input, $flags = 0) - { - $this->failOnBOM($input); - - $this->flags = $flags; - - $this->stack = array(0); - $this->vstack = array(null); - $this->lstack = array(); - - $yytext = ''; - $yylineno = 0; - $yyleng = 0; - $recovering = 0; - $TERROR = 2; - $EOF = 1; - - $this->lexer = new Lexer(); - $this->lexer->setInput($input); - - $yyloc = $this->lexer->yylloc; - $this->lstack[] = $yyloc; - - $symbol = null; - $preErrorSymbol = null; - $state = null; - $action = null; - $a = null; - $r = null; - $yyval = new stdClass; - $p = null; - $len = null; - $newState = null; - $expected = null; - $errStr = null; - - while (true) { - // retreive state number from top of stack - $state = $this->stack[count($this->stack)-1]; - - // use default actions if available - if (isset($this->defaultActions[$state])) { - $action = $this->defaultActions[$state]; - } else { - if ($symbol == null) { - $symbol = $this->lex(); - } - // read action for current state and first input - $action = isset($this->table[$state][$symbol]) ? $this->table[$state][$symbol] : false; - } - - // handle parse error - if (!$action || !$action[0]) { - if (!$recovering) { - // Report error - $expected = array(); - foreach ($this->table[$state] as $p => $ignore) { - if (isset($this->terminals_[$p]) && $p > 2) { - $expected[] = "'" . $this->terminals_[$p] . "'"; - } - } - - $message = null; - if (in_array("'STRING'", $expected) && in_array(substr($this->lexer->match, 0, 1), array('"', "'"))) { - $message = "Invalid string"; - if ("'" === substr($this->lexer->match, 0, 1)) { - $message .= ", it appears you used single quotes instead of double quotes"; - } elseif (preg_match('{".+?(\\\\[^"bfnrt/\\\\u])}', $this->lexer->getUpcomingInput(), $match)) { - $message .= ", it appears you have an unescaped backslash at: ".$match[1]; - } elseif (preg_match('{"(?:[^"]+|\\\\")*$}m', $this->lexer->getUpcomingInput())) { - $message .= ", it appears you forgot to terminated the string, or attempted to write a multiline string which is invalid"; - } - } - - $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; - $errStr .= $this->lexer->showPosition() . "\n"; - if ($message) { - $errStr .= $message; - } else { - $errStr .= (count($expected) > 1) ? "Expected one of: " : "Expected: "; - $errStr .= implode(', ', $expected); - } - - if (',' === substr(trim($this->lexer->getPastInput()), -1)) { - $errStr .= " - It appears you have an extra trailing comma"; - } - - $this->parseError($errStr, array( - 'text' => $this->lexer->match, - 'token' => !empty($this->terminals_[$symbol]) ? $this->terminals_[$symbol] : $symbol, - 'line' => $this->lexer->yylineno, - 'loc' => $yyloc, - 'expected' => $expected, - )); - } - - // just recovered from another error - if ($recovering == 3) { - if ($symbol == $EOF) { - throw new ParsingException($errStr ?: 'Parsing halted.'); - } - - // discard current lookahead and grab another - $yyleng = $this->lexer->yyleng; - $yytext = $this->lexer->yytext; - $yylineno = $this->lexer->yylineno; - $yyloc = $this->lexer->yylloc; - $symbol = $this->lex(); - } - - // try to recover from error - while (true) { - // check for error recovery rule in this state - if (array_key_exists($TERROR, $this->table[$state])) { - break; - } - if ($state == 0) { - throw new ParsingException($errStr ?: 'Parsing halted.'); - } - $this->popStack(1); - $state = $this->stack[count($this->stack)-1]; - } - - $preErrorSymbol = $symbol; // save the lookahead token - $symbol = $TERROR; // insert generic error symbol as new lookahead - $state = $this->stack[count($this->stack)-1]; - $action = isset($this->table[$state][$TERROR]) ? $this->table[$state][$TERROR] : false; - $recovering = 3; // allow 3 real symbols to be shifted before reporting a new error - } - - // this shouldn't happen, unless resolve defaults are off - if (is_array($action[0]) && count($action) > 1) { - throw new ParsingException('Parse Error: multiple actions possible at state: ' . $state . ', token: ' . $symbol); - } - - switch ($action[0]) { - case 1: // shift - $this->stack[] = $symbol; - $this->vstack[] = $this->lexer->yytext; - $this->lstack[] = $this->lexer->yylloc; - $this->stack[] = $action[1]; // push state - $symbol = null; - if (!$preErrorSymbol) { // normal execution/no error - $yyleng = $this->lexer->yyleng; - $yytext = $this->lexer->yytext; - $yylineno = $this->lexer->yylineno; - $yyloc = $this->lexer->yylloc; - if ($recovering > 0) { - $recovering--; - } - } else { // error just occurred, resume old lookahead f/ before error - $symbol = $preErrorSymbol; - $preErrorSymbol = null; - } - break; - - case 2: // reduce - $len = $this->productions_[$action[1]][1]; - - // perform semantic action - $yyval->token = $this->vstack[count($this->vstack) - $len]; // default to $$ = $1 - // default location, uses first token for firsts, last for lasts - $yyval->store = array( // _$ = store - 'first_line' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_line'], - 'last_line' => $this->lstack[count($this->lstack) - 1]['last_line'], - 'first_column' => $this->lstack[count($this->lstack) - ($len ?: 1)]['first_column'], - 'last_column' => $this->lstack[count($this->lstack) - 1]['last_column'], - ); - $r = $this->performAction($yyval, $yytext, $yyleng, $yylineno, $action[1], $this->vstack, $this->lstack); - - if (!$r instanceof Undefined) { - return $r; - } - - if ($len) { - $this->popStack($len); - } - - $this->stack[] = $this->productions_[$action[1]][0]; // push nonterminal (reduce) - $this->vstack[] = $yyval->token; - $this->lstack[] = $yyval->store; - $newState = $this->table[$this->stack[count($this->stack)-2]][$this->stack[count($this->stack)-1]]; - $this->stack[] = $newState; - break; - - case 3: // accept - - return true; - } - } - - return true; - } - - protected function parseError($str, $hash) - { - throw new ParsingException($str, $hash); - } - - // $$ = $tokens // needs to be passed by ref? - // $ = $token - // _$ removed, useless? - private function performAction(stdClass $yyval, $yytext, $yyleng, $yylineno, $yystate, &$tokens) - { - // $0 = $len - $len = count($tokens) - 1; - switch ($yystate) { - case 1: - $yytext = preg_replace_callback('{(?:\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4})}', array($this, 'stringInterpolation'), $yytext); - $yyval->token = $yytext; - break; - case 2: - if (strpos($yytext, 'e') !== false || strpos($yytext, 'E') !== false) { - $yyval->token = floatval($yytext); - } else { - $yyval->token = strpos($yytext, '.') === false ? intval($yytext) : floatval($yytext); - } - break; - case 3: - $yyval->token = null; - break; - case 4: - $yyval->token = true; - break; - case 5: - $yyval->token = false; - break; - case 6: - return $yyval->token = $tokens[$len-1]; - case 13: - $yyval->token = new stdClass; - break; - case 14: - $yyval->token = $tokens[$len-1]; - break; - case 15: - $yyval->token = array($tokens[$len-2], $tokens[$len]); - break; - case 16: - $yyval->token = new stdClass; - $property = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; - $yyval->token->$property = $tokens[$len][1]; - break; - case 17: - $yyval->token = $tokens[$len-2]; - $key = $tokens[$len][0] === '' ? '_empty_' : $tokens[$len][0]; - if (($this->flags & self::DETECT_KEY_CONFLICTS) && isset($tokens[$len-2]->{$key})) { - $errStr = 'Parse error on line ' . ($yylineno+1) . ":\n"; - $errStr .= $this->lexer->showPosition() . "\n"; - $errStr .= "Duplicate key: ".$tokens[$len][0]; - throw new ParsingException($errStr); - } elseif (($this->flags & self::ALLOW_DUPLICATE_KEYS) && isset($tokens[$len-2]->{$key})) { - $duplicateCount = 1; - do { - $duplicateKey = $key . '.' . $duplicateCount++; - } while (isset($tokens[$len-2]->$duplicateKey)); - $key = $duplicateKey; - } - $tokens[$len-2]->$key = $tokens[$len][1]; - break; - case 18: - $yyval->token = array(); - break; - case 19: - $yyval->token = $tokens[$len-1]; - break; - case 20: - $yyval->token = array($tokens[$len]); - break; - case 21: - $tokens[$len-2][] = $tokens[$len]; - $yyval->token = $tokens[$len-2]; - break; - } - - return new Undefined(); - } - - private function stringInterpolation($match) - { - switch ($match[0]) { - case '\\\\': - return '\\'; - case '\"': - return '"'; - case '\b': - return chr(8); - case '\f': - return chr(12); - case '\n': - return "\n"; - case '\r': - return "\r"; - case '\t': - return "\t"; - case '\/': - return "/"; - default: - return html_entity_decode('&#x'.ltrim(substr($match[0], 2), '0').';', 0, 'UTF-8'); - } - } - - private function popStack($n) - { - $this->stack = array_slice($this->stack, 0, - (2 * $n)); - $this->vstack = array_slice($this->vstack, 0, - $n); - $this->lstack = array_slice($this->lstack, 0, - $n); - } - - private function lex() - { - $token = $this->lexer->lex() ?: 1; // $end = 1 - // if token isn't its numeric value, convert - if (!is_numeric($token)) { - $token = isset($this->symbols[$token]) ? $this->symbols[$token] : $token; - } - - return $token; - } - - private function failOnBOM($input) - { - // UTF-8 ByteOrderMark sequence - $bom = "\xEF\xBB\xBF"; - - if (substr($input, 0, 3) === $bom) { - $this->parseError("BOM detected, make sure your input does not include a Unicode Byte-Order-Mark", array()); - } - } -} diff --git a/core/lib/Seld/JsonLint/LICENSE b/core/lib/Seld/JsonLint/LICENSE deleted file mode 100755 index c2344874d..000000000 --- a/core/lib/Seld/JsonLint/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011 Jordi Boggiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/Seld/JsonLint/Lexer.php b/core/lib/Seld/JsonLint/Lexer.php deleted file mode 100755 index a982c498c..000000000 --- a/core/lib/Seld/JsonLint/Lexer.php +++ /dev/null @@ -1,229 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Seld\JsonLint; - -/** - * Lexer class - * - * Ported from https://github.com/zaach/jsonlint - */ -class Lexer -{ - private $EOF = 1; - private $rules = array( - 0 => '/^\s+/', - 1 => '/^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][+-]?[0-9]+)?\b/', - 2 => '{^"(\\\\["bfnrt/\\\\]|\\\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f\\\\"])*"}', - 3 => '/^\{/', - 4 => '/^\}/', - 5 => '/^\[/', - 6 => '/^\]/', - 7 => '/^,/', - 8 => '/^:/', - 9 => '/^true\b/', - 10 => '/^false\b/', - 11 => '/^null\b/', - 12 => '/^$/', - 13 => '/^./', - ); - - private $conditions = array( - "INITIAL" => array( - "rules" => array(0,1,2,3,4,5,6,7,8,9,10,11,12,13), - "inclusive" => true, - ), - ); - - private $conditionStack; - private $input; - private $more; - private $done; - private $matched; - - public $match; - public $yylineno; - public $yyleng; - public $yytext; - public $yylloc; - - public function lex() - { - $r = $this->next(); - if (!$r instanceof Undefined) { - return $r; - } - - return $this->lex(); - } - - public function setInput($input) - { - $this->input = $input; - $this->more = false; - $this->done = false; - $this->yylineno = $this->yyleng = 0; - $this->yytext = $this->matched = $this->match = ''; - $this->conditionStack = array('INITIAL'); - $this->yylloc = array('first_line' => 1, 'first_column' => 0, 'last_line' => 1, 'last_column' => 0); - - return $this; - } - - public function showPosition() - { - $pre = str_replace("\n", '', $this->getPastInput()); - $c = str_repeat('-', strlen($pre)); // new Array(pre.length + 1).join("-"); - - return $pre . str_replace("\n", '', $this->getUpcomingInput()) . "\n" . $c . "^"; - } - - public function getPastInput() - { - $past = substr($this->matched, 0, strlen($this->matched) - strlen($this->match)); - - return (strlen($past) > 20 ? '...' : '') . substr($past, -20); - } - - public function getUpcomingInput() - { - $next = $this->match; - if (strlen($next) < 20) { - $next .= substr($this->input, 0, 20 - strlen($next)); - } - - return substr($next, 0, 20) . (strlen($next) > 20 ? '...' : ''); - } - - protected function parseError($str, $hash) - { - throw new \Exception($str); - } - - private function next() - { - if ($this->done) { - return $this->EOF; - } - if (!$this->input) { - $this->done = true; - } - - $token = null; - $match = null; - $col = null; - $lines = null; - - if (!$this->more) { - $this->yytext = ''; - $this->match = ''; - } - - $rules = $this->getCurrentRules(); - $rulesLen = count($rules); - - for ($i=0; $i < $rulesLen; $i++) { - if (preg_match($this->rules[$rules[$i]], $this->input, $match)) { - preg_match_all('/\n.*/', $match[0], $lines); - $lines = $lines[0]; - if ($lines) { - $this->yylineno += count($lines); - } - - $this->yylloc = array( - 'first_line' => $this->yylloc['last_line'], - 'last_line' => $this->yylineno+1, - 'first_column' => $this->yylloc['last_column'], - 'last_column' => $lines ? strlen($lines[count($lines) - 1]) - 1 : $this->yylloc['last_column'] + strlen($match[0]), - ); - $this->yytext .= $match[0]; - $this->match .= $match[0]; - $this->matches = $match; - $this->yyleng = strlen($this->yytext); - $this->more = false; - $this->input = substr($this->input, strlen($match[0])); - $this->matched .= $match[0]; - $token = $this->performAction($rules[$i], $this->conditionStack[count($this->conditionStack)-1]); - if ($token) { - return $token; - } - - return new Undefined(); - } - } - - if ($this->input === "") { - return $this->EOF; - } - - $this->parseError( - 'Lexical error on line ' . ($this->yylineno+1) . ". Unrecognized text.\n" . $this->showPosition(), - array( - 'text' => "", - 'token' => null, - 'line' => $this->yylineno, - ) - ); - } - - private function begin($condition) - { - $this->conditionStack[] = $condition; - } - - private function popState() - { - return array_pop($this->conditionStack); - } - - private function getCurrentRules() - { - return $this->conditions[$this->conditionStack[count($this->conditionStack)-1]]['rules']; - } - - private function performAction($avoiding_name_collisions, $YY_START) - { - $YYSTATE = $YY_START; - switch ($avoiding_name_collisions) { - case 0:/* skip whitespace */ - break; - case 1: - return 6; - break; - case 2: - $this->yytext = substr($this->yytext, 1, $this->yyleng-2); - - return 4; - case 3: - return 17; - case 4: - return 18; - case 5: - return 23; - case 6: - return 24; - case 7: - return 22; - case 8: - return 21; - case 9: - return 10; - case 10: - return 11; - case 11: - return 8; - case 12: - return 14; - case 13: - return 'INVALID'; - } - } -} diff --git a/core/lib/Seld/JsonLint/ParsingException.php b/core/lib/Seld/JsonLint/ParsingException.php deleted file mode 100755 index 6562ef5ea..000000000 --- a/core/lib/Seld/JsonLint/ParsingException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Seld\JsonLint; - -class ParsingException extends \Exception -{ - protected $details; - - public function __construct($message, $details = array()) - { - $this->details = $details; - parent::__construct($message); - } - - public function getDetails() - { - return $this->details; - } -} diff --git a/core/lib/Seld/JsonLint/Undefined.php b/core/lib/Seld/JsonLint/Undefined.php deleted file mode 100755 index e83d5be69..000000000 --- a/core/lib/Seld/JsonLint/Undefined.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Seld\JsonLint; - -class Undefined -{ -} diff --git a/core/lib/Wrench/Application/Application.php b/core/lib/Wrench/Application/Application.php deleted file mode 100755 index 2132a8907..000000000 --- a/core/lib/Wrench/Application/Application.php +++ /dev/null @@ -1,34 +0,0 @@ -newlines = $newlines; - - if (file_exists(__DIR__."/../../../../public/latest-change.txt")) { - $this->savedTimestamp = file_get_contents(__DIR__."/../../../../public/latest-change.txt"); - } else { - $this->savedTimestamp = time(); - } - - } - - /** - * When a client connects add it to the list of connected clients - */ - public function onConnect($client) { - $id = $client->getId(); - $this->clients[$id] = $client; - } - - /** - * When a client disconnects remove it from the list of connected clients - */ - public function onDisconnect($client) { - $id = $client->getId(); - unset($this->clients[$id]); - } - - /** - * Dead function in this instance - */ - public function onData($data, $client) { - // function not in use - } - - /** - * Sends out a message once a second to all connected clients containing the contents of latest-change.txt - */ - public function onUpdate() { - - if (file_exists(__DIR__."/../../../../public/latest-change.txt")) { - $readTimestamp = file_get_contents(__DIR__."/../../../../public/latest-change.txt"); - if ($readTimestamp != $this->savedTimestamp) { - print "pattern lab updated. alerting connected browsers...\n"; - foreach ($this->clients as $sendto) { - $sendto->send($readTimestamp); - } - $this->savedTimestamp = $readTimestamp; - } else { - if ($this->newlines) print "\n"; - } - } - - } - -} diff --git a/core/lib/Wrench/Application/PageFollowApplication.php b/core/lib/Wrench/Application/PageFollowApplication.php deleted file mode 100644 index 9306b64e7..000000000 --- a/core/lib/Wrench/Application/PageFollowApplication.php +++ /dev/null @@ -1,71 +0,0 @@ -getId(); - $this->clients[$id] = $client; - if ($this->data != null) { - $client->send(json_encode($this->data)); - } - } - - /** - * When a client disconnects remove it from the list of connected clients - */ - public function onDisconnect($client) { - $id = $client->getId(); - unset($this->clients[$id]); - } - - /** - * When receiving a message from a client, strip it to avoid cross-domain issues and send it to all clients except the one who sent it - * Also store the address as the current address for any new clients that attach - */ - public function onData($data, $client) { - - $dataDecoded = json_decode($data); - - $dataDecoded->url = "/".$dataDecoded->url; - $dataEncoded = json_encode($dataDecoded); - $testId = $client->getId(); - foreach ($this->clients as $sendto) { - if ($testId != $sendto->getId()) { - $sendto->send($dataEncoded); - } - } - - $this->data = $dataDecoded; - - } - - /** - * Dead function in this instance - */ - public function onUpdate() { - // not using for this application - } - -} diff --git a/core/lib/Wrench/BasicServer.php b/core/lib/Wrench/BasicServer.php deleted file mode 100755 index 88578a055..000000000 --- a/core/lib/Wrench/BasicServer.php +++ /dev/null @@ -1,70 +0,0 @@ -configureRateLimiter(); - $this->configureOriginPolicy(); - } - - /** - * @see Wrench.Server::configure() - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'check_origin' => true, - 'allowed_origins' => array(), - 'origin_policy_class' => 'Wrench\Listener\OriginPolicy', - 'rate_limiter_class' => 'Wrench\Listener\RateLimiter' - ), $options); - - parent::configure($options); - } - - protected function configureRateLimiter() - { - $class = $this->options['rate_limiter_class']; - $this->rateLimiter = new $class(); - $this->rateLimiter->listen($this); - } - - /** - * Configures the origin policy - */ - protected function configureOriginPolicy() - { - $class = $this->options['origin_policy_class']; - $this->originPolicy = new $class($this->options['allowed_origins']); - - if ($this->options['check_origin']) { - $this->originPolicy->listen($this); - } - } - - /** - * Adds an allowed origin - * - * @param array $origin - */ - public function addAllowedOrigin($origin) - { - $this->originPolicy->addAllowedOrigin($origin); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Client.php b/core/lib/Wrench/Client.php deleted file mode 100755 index 87e8f11ba..000000000 --- a/core/lib/Wrench/Client.php +++ /dev/null @@ -1,265 +0,0 @@ - - */ - protected $received = array(); - - /** - * Constructor - * - * @param string $uri - * @param string $origin The origin to include in the handshake (required - * in later versions of the protocol) - * @param array $options (optional) Array of options - * - socket => Socket instance (otherwise created) - * - protocol => Protocol - */ - public function __construct($uri, $origin, array $options = array()) - { - parent::__construct($options); - - $uri = (string)$uri; - if (!$uri) { - throw new InvalidArgumentException('No URI specified'); - } - $this->uri = $uri; - - $origin = (string)$origin; - if (!$origin) { - throw new InvalidArgumentException('No origin specified'); - } - $this->origin = $origin; - - $this->protocol->validateUri($this->uri); - $this->protocol->validateOriginUri($this->origin); - - $this->configureSocket(); - $this->configurePayloadHandler(); - } - - /** - * Configure options - * - * @param array $options - * @return void - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'socket_class' => 'Wrench\\Socket\\ClientSocket', - 'on_data_callback' => null - ), $options); - - parent::configure($options); - } - - /** - * Configures the client socket - */ - protected function configureSocket() - { - $class = $this->options['socket_class']; - $this->socket = new $class($this->uri); - } - - /** - * Configures the payload handler - */ - protected function configurePayloadHandler() - { - $this->payloadHandler = new PayloadHandler(array($this, 'onData'), $this->options); - } - - /** - * Payload receiver - * - * Public because called from our PayloadHandler. Don't call us, we'll call - * you (via the on_data_callback option). - * - * @param Payload $payload - */ - public function onData(Payload $payload) - { - $this->received[] = $payload; - if (($callback = $this->options['on_data_callback'])) { - call_user_func($callback, $payload); - } - } - - /** - * Adds a request header to be included in the initial handshake - * - * For example, to include a Cookie header - * - * @param string $name - * @param string $value - * @return void - */ - public function addRequestHeader($name, $value) - { - $this->headers[$name] = $value; - } - - /** - * Sends data to the socket - * - * @param string $data - * @param string $type Payload type - * @param boolean $masked - * @return boolean Success - */ - public function sendData($data, $type = Protocol::TYPE_TEXT, $masked = true) - { - if (is_string($type) && isset(Protocol::$frameTypes[$type])) { - $type = Protocol::$frameTypes[$type]; - } - - $payload = $this->protocol->getPayload(); - - $payload->encode( - $data, - $type, - $masked - ); - - return $payload->sendToSocket($this->socket); - } - - /** - * Receives data sent by the server - * - * @param callable $callback - * @return array Payload received since the last call to receive() - */ - public function receive() - { - if (!$this->isConnected()) { - return false; - } - - $data = $this->socket->receive(); - - if (!$data) { - return $data; - } - - $old = $this->received; - $this->payloadHandler->handle($data); - return array_diff($this->received, $old); - } - - /** - * Connect to the Wrench server - * - * @return boolean Whether a new connection was made - */ - public function connect() - { - if ($this->isConnected()) { - return false; - } - - $this->socket->connect(); - - $key = $this->protocol->generateKey(); - $handshake = $this->protocol->getRequestHandshake( - $this->uri, - $key, - $this->origin, - $this->headers - ); - - $this->socket->send($handshake); - $response = $this->socket->receive(self::MAX_HANDSHAKE_RESPONSE); - return ($this->connected = - $this->protocol->validateResponseHandshake($response, $key)); - } - - /** - * Whether the client is currently connected - * - * @return boolean - */ - public function isConnected() - { - return $this->connected; - } - - /** - * @todo Bug: what if connect has been called twice. The first socket never - * gets closed. - */ - public function disconnect() - { - if ($this->socket) { - $this->socket->disconnect(); - } - $this->connected = false; - } - - -} diff --git a/core/lib/Wrench/Connection.php b/core/lib/Wrench/Connection.php deleted file mode 100755 index 2538fc739..000000000 --- a/core/lib/Wrench/Connection.php +++ /dev/null @@ -1,538 +0,0 @@ - 'value' - * - * @var array - */ - protected $queryParams = null; - - /** - * Connection ID - * - * @var string|null - */ - protected $id = null; - - /** - * @var PayloadHandler - */ - protected $payloadHandler; - - /** - * Constructor - * - * @param Server $server - * @param ServerClientSocket $socket - * @param array $options - * @throws InvalidArgumentException - */ - public function __construct( - ConnectionManager $manager, - ServerClientSocket $socket, - array $options = array() - ) { - $this->manager = $manager; - $this->socket = $socket; - - - parent::__construct($options); - - $this->configureClientInformation(); - $this->configurePayloadHandler(); - - $this->log('Connected'); - } - - /** - * Gets the connection manager of this connection - * - * @return \Wrench\ConnectionManager - */ - public function getConnectionManager() - { - return $this->manager; - } - - /** - * @see Wrench\Util.Configurable::configure() - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'connection_id_secret' => 'asu5gj656h64Da(0crt8pud%^WAYWW$u76dwb', - 'connection_id_algo' => 'sha512', - ), $options); - - parent::configure($options); - } - - protected function configurePayloadHandler() - { - $this->payloadHandler = new PayloadHandler( - array($this, 'handlePayload'), - $this->options - ); - } - - /** - * @throws RuntimeException - */ - protected function configureClientInformation() - { - $this->ip = $this->socket->getIp(); - $this->port = $this->socket->getPort(); - $this->configureClientId(); - } - - /** - * Configures the client ID - * - * We hash the client ID to prevent leakage of information if another client - * happens to get a hold of an ID. The secret *must* be lengthy, and must - * be kept secret for this to work: otherwise it's trivial to search the space - * of possible IP addresses/ports (well, if not trivial, at least very fast). - */ - protected function configureClientId() - { - $message = sprintf( - '%s:uri=%s&ip=%s&port=%s', - $this->options['connection_id_secret'], - rawurlencode($this->manager->getUri()), - rawurlencode($this->ip), - rawurlencode($this->port) - ); - - $algo = $this->options['connection_id_algo']; - - if (extension_loaded('gmp')) { - $hash = hash($algo, $message); - $hash = gmp_strval(gmp_init('0x' . $hash, 16), 62); - } else { - // @codeCoverageIgnoreStart - $hash = hash($algo, $message); - // @codeCoverageIgnoreEnd - } - - $this->id = $hash; - } - - /** - * Data receiver - * - * Called by the connection manager when the connection has received data - * - * @param string $data - */ - public function onData($data) - { - if (!$this->handshaked) { - return $this->handshake($data); - } - return $this->handle($data); - } - - /** - * Performs a websocket handshake - * - * @param string $data - * @throws BadRequestException - * @throws HandshakeException - * @throws WrenchException - */ - public function handshake($data) - { - try { - list($path, $origin, $key, $extensions, $protocol, $headers, $params) - = $this->protocol->validateRequestHandshake($data); - - $this->headers = $headers; - $this->queryParams = $params; - - $this->application = $this->manager->getApplicationForPath($path); - if (!$this->application) { - throw new BadRequestException('Invalid application'); - } - - $this->manager->getServer()->notify( - Server::EVENT_HANDSHAKE_REQUEST, - array($this, $path, $origin, $key, $extensions) - ); - - $response = $this->protocol->getResponseHandshake($key); - - if (!$this->socket->isConnected()) { - throw new HandshakeException('Socket is not connected'); - } - - if ($this->socket->send($response) === false) { - throw new HandshakeException('Could not send handshake response'); - } - - $this->handshaked = true; - - $this->log(sprintf( - 'Handshake successful: %s:%d (%s) connected to %s', - $this->getIp(), - $this->getPort(), - $this->getId(), - $path - ), 'info'); - - $this->manager->getServer()->notify( - Server::EVENT_HANDSHAKE_SUCCESSFUL, - array($this) - ); - - if (method_exists($this->application, 'onConnect')) { - $this->application->onConnect($this); - } - } catch (WrenchException $e) { - $this->log('Handshake failed: ' . $e, 'err'); - $this->close($e); - } - } - - /** - * Returns a string export of the given binary data - * - * @param string $data - * @return string - */ - protected function export($data) - { - $export = ''; - foreach (str_split($data) as $chr) { - $export .= '\\x' . ord($chr); - } - } - - /** - * Handle data received from the client - * - * The data passed in may belong to several different frames across one or - * more protocols. It may not even contain a single complete frame. This method - * manages slotting the data into separate payload objects. - * - * @todo An endpoint MUST be capable of handling control frames in the - * middle of a fragmented message. - * @param string $data - * @return void - */ - public function handle($data) - { - $this->payloadHandler->handle($data); - } - - /** - * Handle a complete payload received from the client - * - * Public because called from our PayloadHandler - * - * @param string $payload - */ - public function handlePayload(Payload $payload) - { - $app = $this->getClientApplication(); - - $this->log('Handling payload: ' . $payload->getPayload(), 'debug'); - - switch ($type = $payload->getType()) { - case Protocol::TYPE_TEXT: - if (method_exists($app, 'onData')) { - $app->onData($payload, $this); - } - return; - - case Protocol::TYPE_BINARY: - if(method_exists($app, 'onBinaryData')) { - $app->onBinaryData($payload, $this); - } else { - $this->close(1003); - } - break; - - case Protocol::TYPE_PING: - $this->log('Ping received', 'notice'); - $this->send($payload->getPayload(), Protocol::TYPE_PONG); - $this->log('Pong!', 'debug'); - break; - - /** - * A Pong frame MAY be sent unsolicited. This serves as a - * unidirectional heartbeat. A response to an unsolicited Pong - * frame is not expected. - */ - case Protocol::TYPE_PONG: - $this->log('Received unsolicited pong', 'info'); - break; - - case Protocol::TYPE_CLOSE: - $this->log('Close frame received', 'notice'); - $this->close(); - $this->log('Disconnected', 'info'); - break; - - default: - throw new ConnectionException('Unhandled payload type'); - } - } - - /** - * Sends the payload to the connection - * - * @param string $payload - * @param string $type - * @throws HandshakeException - * @throws ConnectionException - * @return boolean - */ - public function send($data, $type = Protocol::TYPE_TEXT) - { - if (!$this->handshaked) { - throw new HandshakeException('Connection is not handshaked'); - } - - $payload = $this->protocol->getPayload(); - - // Servers don't send masked payloads - $payload->encode($data, $type, false); - - if (!$payload->sendToSocket($this->socket)) { - $this->log('Could not send payload to client', 'warn'); - throw new ConnectionException('Could not send data to connection: ' . $this->socket->getLastError()); - } - - return true; - } - - /** - * Processes data on the socket - * - * @throws CloseException - */ - public function process() - { - $data = $this->socket->receive(); - $bytes = strlen($data); - - if ($bytes === 0 || $data === false) { - throw new CloseException('Error reading data from socket: ' . $this->socket->getLastError()); - } - - $this->onData($data); - } - - /** - * Closes the connection according to the WebSocket protocol - * - * If an endpoint receives a Close frame and that endpoint did not - * previously send a Close frame, the endpoint MUST send a Close frame - * in response. It SHOULD do so as soon as is practical. An endpoint - * MAY delay sending a close frame until its current message is sent - * (for instance, if the majority of a fragmented message is already - * sent, an endpoint MAY send the remaining fragments before sending a - * Close frame). However, there is no guarantee that the endpoint which - * has already sent a Close frame will continue to process data. - - * After both sending and receiving a close message, an endpoint - * considers the WebSocket connection closed, and MUST close the - * underlying TCP connection. The server MUST close the underlying TCP - * connection immediately; the client SHOULD wait for the server to - * close the connection but MAY close the connection at any time after - * sending and receiving a close message, e.g. if it has not received a - * TCP close from the server in a reasonable time period. - * - * @param int|Exception $statusCode - * @return boolean - */ - public function close($code = Protocol::CLOSE_NORMAL) - { - try { - if (!$this->handshaked) { - $response = $this->protocol->getResponseError($code); - $this->socket->send($response); - } else { - $response = $this->protocol->getCloseFrame($code); - $this->socket->send($response); - } - } catch (Exception $e) { - $this->log('Unable to send close message', 'warning'); - } - - if ($this->application && method_exists($this->application, 'onDisconnect')) { - $this->application->onDisconnect($this); - } - - $this->socket->disconnect(); - $this->manager->removeConnection($this); - } - - /** - * Logs a message - * - * @param string $message - * @param string $priority - */ - public function log($message, $priority = 'info') - { - $this->manager->log(sprintf( - '%s: %s:%d (%s): %s', - __CLASS__, - $this->getIp(), - $this->getPort(), - $this->getId(), - $message - ), $priority); - } - - /** - * Gets the IP address of the connection - * - * @return string Usually dotted quad notation - */ - public function getIp() - { - return $this->ip; - } - - /** - * Gets the port of the connection - * - * @return int - */ - public function getPort() - { - return $this->port; - } - - /** - * Gets the non-web-sockets headers included with the original request - * - * @return array - */ - public function getHeaders() - { - return $this->headers; - } - - /** - * Gets the query parameters included with the original request - * - * @return array - */ - public function getQueryParams() - { - return $this->queryParams; - } - - /** - * Gets the connection ID - * - * @return string - */ - public function getId() - { - return $this->id; - } - - /** - * Gets the socket object - * - * @return Socket\ServerClientSocket - */ - public function getSocket() - { - return $this->socket; - } - - /** - * Gets the client application - * - * @return Application - */ - public function getClientApplication() - { - return (isset($this->application)) ? $this->application : false; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/ConnectionManager.php b/core/lib/Wrench/ConnectionManager.php deleted file mode 100755 index 8a503de03..000000000 --- a/core/lib/Wrench/ConnectionManager.php +++ /dev/null @@ -1,332 +0,0 @@ - Connection> - */ - protected $connections = array(); - - /** - * An array of raw socket resources, corresponding to connections, roughly - * - * @var array resource> - */ - protected $resources = array(); - - /** - * Constructor - * - * @param Server $server - * @param array $options - */ - public function __construct(Server $server, array $options = array()) - { - $this->server = $server; - - parent::__construct($options); - } - - /** - * @see Countable::count() - */ - public function count() - { - return count($this->connections); - } - - /** - * @see Wrench\Socket.Socket::configure() - * Options include: - * - timeout_select => int, seconds, default 0 - * - timeout_select_microsec => int, microseconds (NB: not milli), default: 200000 - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'socket_master_class' => 'Wrench\Socket\ServerSocket', - 'socket_master_options' => array(), - 'socket_client_class' => 'Wrench\Socket\ServerClientSocket', - 'socket_client_options' => array(), - 'connection_class' => 'Wrench\Connection', - 'connection_options' => array(), - 'timeout_select' => self::TIMEOUT_SELECT, - 'timeout_select_microsec' => self::TIMEOUT_SELECT_MICROSEC - ), $options); - - parent::configure($options); - - $this->configureMasterSocket(); - } - - /** - * Gets the application associated with the given path - * - * @param string $path - */ - public function getApplicationForPath($path) - { - $path = ltrim($path, '/'); - return $this->server->getApplication($path); - } - - /** - * Configures the main server socket - * - * @param string $uri - */ - protected function configureMasterSocket() - { - $class = $this->options['socket_master_class']; - $options = $this->options['socket_master_options']; - $this->socket = new $class($this->server->getUri(), $options); - } - - /** - * Listens on the main socket - * - * @return void - */ - public function listen() - { - $this->socket->listen(); - $this->resources[$this->socket->getResourceId()] = $this->socket->getResource(); - } - - /** - * Gets all resources - * - * @return array resource) - */ - protected function getAllResources() - { - return array_merge($this->resources, array( - $this->socket->getResourceId() => $this->socket->getResource() - )); - } - - /** - * Returns the Connection associated with the specified socket resource - * - * @param resource $socket - * @return Connection - */ - protected function getConnectionForClientSocket($socket) - { - if (!isset($this->connections[$this->resourceId($socket)])) { - return false; - } - return $this->connections[$this->resourceId($socket)]; - } - - /** - * Select and process an array of resources - * - * @param array $resources - */ - public function selectAndProcess() - { - $read = $this->resources; - $unused_write = null; - $unsued_exception = null; - - stream_select( - $read, - $unused_write, - $unused_exception, - $this->options['timeout_select'], - $this->options['timeout_select_microsec'] - ); - - foreach ($read as $socket) { - if ($socket == $this->socket->getResource()) { - $this->processMasterSocket(); - } else { - $this->processClientSocket($socket); - } - } - } - - /** - * Process events on the master socket ($this->socket) - * - * @return void - */ - protected function processMasterSocket() - { - $new = null; - - try { - $new = $this->socket->accept(); - } catch (Exception $e) { - $this->server->log('Socket error: ' . $e, 'err'); - return; - } - - $connection = $this->createConnection($new); - $this->server->notify(Server::EVENT_SOCKET_CONNECT, array($new, $connection)); - } - - /** - * Creates a connection from a socket resource - * - * The create connection object is based on the options passed into the - * constructor ('connection_class', 'connection_options'). This connection - * instance and its associated socket resource are then stored in the - * manager. - * - * @param resource $resource A socket resource - * @return Connection - */ - protected function createConnection($resource) - { - if (!$resource || !is_resource($resource)) { - throw new InvalidArgumentException('Invalid connection resource'); - } - - $socket_class = $this->options['socket_client_class']; - $socket_options = $this->options['socket_client_options']; - - $connection_class = $this->options['connection_class']; - $connection_options = $this->options['connection_options']; - - $socket = new $socket_class($resource, $socket_options); - $connection = new $connection_class($this, $socket, $connection_options); - - $id = $this->resourceId($resource); - $this->resources[$id] = $resource; - $this->connections[$id] = $connection; - - return $connection; - } - - /** - * Process events on a client socket - * - * @param resource $socket - */ - protected function processClientSocket($socket) - { - $connection = $this->getConnectionForClientSocket($socket); - - if (!$connection) { - $this->log('No connection for client socket', 'warning'); - return; - } - - try { - $connection->process(); - } catch (CloseException $e) { - $this->log('Client connection closed: ' . $e, 'notice'); - $connection->close($e); - } catch (WrenchException $e) { - $this->log('Error on client socket: ' . $e, 'warning'); - $connection->close($e); - } - } - - /** - * This server makes an explicit assumption: PHP resource types may be cast - * to a integer. Furthermore, we assume this is bijective. Both seem to be - * true in most circumstances, but may not be guaranteed. - * - * This method (and $this->getResourceId()) exist to make this assumption - * explicit. - * - * This is needed on the connection manager as well as on resources - * - * @param resource $resource - */ - protected function resourceId($resource) - { - return (int)$resource; - } - - /** - * Gets the connection manager's listening URI - * - * @return string - */ - public function getUri() - { - return $this->server->getUri(); - } - - /** - * Logs a message - * - * @param string $message - * @param string $priority - */ - public function log($message, $priority = 'info') - { - $this->server->log(sprintf( - '%s: %s', - __CLASS__, - $message - ), $priority); - } - - /** - * @return \Wrench\Server - */ - public function getServer() - { - return $this->server; - } - - /** - * Removes a connection - * - * @param Connection $connection - */ - public function removeConnection(Connection $connection) - { - $socket = $connection->getSocket(); - - if ($socket->getResource()) { - $index = $socket->getResourceId(); - } else { - $index = array_search($connection, $this->connections); - } - - if (!$index) { - $this->log('Could not remove connection: not found', 'warning'); - } - - unset($this->connections[$index]); - unset($this->resources[$index]); - - $this->server->notify( - Server::EVENT_SOCKET_DISCONNECT, - array($connection->getSocket(), $connection) - ); - } -} diff --git a/core/lib/Wrench/Exception/BadRequestException.php b/core/lib/Wrench/Exception/BadRequestException.php deleted file mode 100755 index 8b8a1591f..000000000 --- a/core/lib/Wrench/Exception/BadRequestException.php +++ /dev/null @@ -1,22 +0,0 @@ -buffer) { - return false; - } - - try { - return $this->getBufferLength() >= $this->getExpectedBufferLength(); - } catch (FrameException $e) { - return false; - } - } - - /** - * Receieves data into the frame - * - * @param string $buffer - */ - public function receiveData($data) - { - $this->buffer .= $data; - } - - /** - * Gets the remaining number of bytes before this frame will be complete - * - * @return number - */ - public function getRemainingData() - { - try { - return $this->getExpectedBufferLength() - $this->getBufferLength(); - } catch (FrameException $e) { - return null; - } - } - - /** - * Whether this frame is waiting for more data - * - * @return boolean - */ - public function isWaitingForData() - { - return $this->getRemainingData() > 0; - } - - /** - * Gets the contents of the frame payload - * - * The frame must be complete to call this method. - * - * @return string - */ - public function getFramePayload() - { - if (!$this->isComplete()) { - throw new FrameException('Cannot get payload: frame is not complete'); - } - - if (!$this->payload && $this->buffer) { - $this->decodeFramePayloadFromBuffer(); - } - - return $this->payload; - } - - /** - * Gets the contents of the frame buffer - * - * This is the encoded value, receieved into the frame with receiveData(). - * - * @throws FrameException - * @return string binary - */ - public function getFrameBuffer() - { - if (!$this->buffer && $this->payload) { - throw new FrameException('Cannot get frame buffer'); - } - return $this->buffer; - } - - /** - * Gets the expected length of the frame payload - * - * @return int - */ - protected function getBufferLength() - { - return strlen($this->buffer); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Frame/HybiFrame.php b/core/lib/Wrench/Frame/HybiFrame.php deleted file mode 100755 index da6377d7e..000000000 --- a/core/lib/Wrench/Frame/HybiFrame.php +++ /dev/null @@ -1,376 +0,0 @@ -= 0 - */ - public function encode($payload, $type = Protocol::TYPE_TEXT, $masked = false) - { - if (!is_int($type) || !in_array($type, Protocol::$frameTypes)) { - throw new InvalidArgumentException('Invalid frame type'); - } - - $this->type = $type; - $this->masked = $masked; - $this->payload = $payload; - $this->length = strlen($this->payload); - $this->offset_mask = null; - $this->offset_payload = null; - - $this->buffer = "\x00\x00"; - - $this->buffer[self::BYTE_HEADER] = chr( - (self::BITFIELD_TYPE & $this->type) - | (self::BITFIELD_FINAL & PHP_INT_MAX) - ); - - $masked_bit = (self::BITFIELD_MASKED & ($this->masked ? PHP_INT_MAX : 0)); - - if ($this->length <= 125) { - $this->buffer[self::BYTE_INITIAL_LENGTH] = chr( - (self::BITFIELD_INITIAL_LENGTH & $this->length) | $masked_bit - ); - } elseif ($this->length <= 65536) { - $this->buffer[self::BYTE_INITIAL_LENGTH] = chr( - (self::BITFIELD_INITIAL_LENGTH & 126) | $masked_bit - ); - $this->buffer .= pack('n', $this->length); - } else { - $this->buffer[self::BYTE_INITIAL_LENGTH] = chr( - (self::BITFIELD_INITIAL_LENGTH & 127) | $masked_bit - ); - - if (PHP_INT_MAX > 2147483647) { - $this->buffer .= pack('NN', $this->length >> 32, $this->length); - // $this->buffer .= pack('I', $this->length); - } else { - $this->buffer .= pack('NN', 0, $this->length); - } - } - - if ($this->masked) { - $this->mask = $this->generateMask(); - $this->buffer .= $this->mask; - $this->buffer .= $this->mask($this->payload); - } else { - $this->buffer .= $this->payload; - } - - $this->offset_mask = $this->getMaskOffset(); - $this->offset_payload = $this->getPayloadOffset(); - - return $this; - } - - /** - * Masks/Unmasks the frame - * - * @param string $payload - * @return string - */ - protected function mask($payload) - { - $length = strlen($payload); - $mask = $this->getMask(); - - $unmasked = ''; - for ($i = 0; $i < $length; $i++) { - $unmasked .= $payload[$i] ^ $mask[$i % 4]; - } - - return $unmasked; - } - - /** - * Masks a payload - * - * @param string $payload - * @return string - */ - protected function unmask($payload) - { - return $this->mask($payload); - } - - public function receiveData($data) - { - if ($this->getBufferLength() <= self::BYTE_INITIAL_LENGTH) { - $this->length = null; - $this->offset_payload = null; - } - parent::receiveData($data); - } - - /** - * Gets the mask - * - * @throws FrameException - * @return string - */ - protected function getMask() - { - if (!isset($this->mask)) { - if (!$this->isMasked()) { - throw new FrameException('Cannot get mask: frame is not masked'); - } - $this->mask = substr($this->buffer, $this->getMaskOffset(), $this->getMaskSize()); - } - return $this->mask; - } - - /** - * Generates a suitable masking key - * - * @return string - */ - protected function generateMask() - { - if (extension_loaded('openssl')) { - return openssl_random_pseudo_bytes(4); - } else { - // SHA1 is 128 bit (= 16 bytes) - // So we pack it into 32 bits - return pack('N', sha1(spl_object_hash($this) . mt_rand(0, PHP_INT_MAX) . uniqid('', true), true)); - } - } - - /** - * Whether the frame is masked - * - * @return boolean - */ - public function isMasked() - { - if (!isset($this->masked)) { - if (!isset($this->buffer[1])) { - throw new FrameException('Cannot tell if frame is masked: not enough frame data received'); - } - $this->masked = (boolean)(ord($this->buffer[1]) & self::BITFIELD_MASKED); - } - return $this->masked; - } - - /** - * @see Wrench\Frame.Frame::getExpectedDataLength() - */ - protected function getExpectedBufferLength() - { - return $this->getLength() + $this->getPayloadOffset(); - } - - /** - * Gets the offset of the payload in the frame - * - * @return int - */ - protected function getPayloadOffset() - { - if (!isset($this->offset_payload)) { - $offset = $this->getMaskOffset(); - $offset += $this->getMaskSize(); - - $this->offset_payload = $offset; - } - return $this->offset_payload; - } - - /** - * Gets the offset in the frame to the masking bytes - * - * @return int - */ - protected function getMaskOffset() - { - if (!isset($this->offset_mask)) { - $offset = self::BYTE_INITIAL_LENGTH + 1; - $offset += $this->getLengthSize(); - - $this->offset_mask = $offset; - } - return $this->offset_mask; - } - - /** - * @see Wrench\Frame.Frame::getLength() - */ - public function getLength() - { - if (!$this->length) { - $initial = $this->getInitialLength(); - - if ($initial < 126) { - $this->length = $initial; - } elseif ($initial >= 126) { - // Extended payload length: 2 or 8 bytes - $start = self::BYTE_INITIAL_LENGTH + 1; - $end = self::BYTE_INITIAL_LENGTH + $this->getLengthSize(); - - if ($end > $this->getBufferLength()) { - throw new FrameException('Cannot get extended length: need more data'); - } - - $length = 0; - for ($i = $start; $i <= $end; $i++) { - $length <<= 8; - $length += ord($this->buffer[$i]); - } - - $this->length = $length; - } - } - return $this->length; - } - - /** - * Gets the inital length value, stored in the first length byte - * - * This determines how the rest of the length value is parsed out of the - * frame. - * - * @return int - */ - protected function getInitialLength() - { - if (!isset($this->buffer[self::BYTE_INITIAL_LENGTH])) { - throw new FrameException('Cannot yet tell expected length'); - } - $a = (int)(ord($this->buffer[self::BYTE_INITIAL_LENGTH]) & self::BITFIELD_INITIAL_LENGTH); - - return (int)(ord($this->buffer[self::BYTE_INITIAL_LENGTH]) & self::BITFIELD_INITIAL_LENGTH); - } - - /** - * Returns the byte size of the length part of the frame - * - * Not including the initial 7 bit part - * - * @return int - */ - protected function getLengthSize() - { - $initial = $this->getInitialLength(); - - if ($initial < 126) { - return 0; - } elseif ($initial === 126) { - return 2; - } elseif ($initial === 127) { - return 8; - } - } - - /** - * Returns the byte size of the mask part of the frame - * - * @return int - */ - protected function getMaskSize() - { - if ($this->isMasked()) { - return 4; - } - return 0; - } - - /** - * @see Wrench\Frame.Frame::decodeFramePayloadFromBuffer() - */ - protected function decodeFramePayloadFromBuffer() - { - $payload = substr($this->buffer, $this->getPayloadOffset()); - - if ($this->isMasked()) { - $payload = $this->unmask($payload); - } - - $this->payload = $payload; - } - - /** - * @see Wrench\Frame.Frame::isFinal() - */ - public function isFinal() - { - if (!isset($this->buffer[self::BYTE_HEADER])) { - throw new FrameException('Cannot yet tell if frame is final'); - } - return (boolean)(ord($this->buffer[self::BYTE_HEADER]) & self::BITFIELD_FINAL); - } - - /** - * @throws FrameException - * @see Wrench\Frame.Frame::getType() - */ - public function getType() - { - if (!isset($this->buffer[self::BYTE_HEADER])) { - throw new FrameException('Cannot yet tell type of frame'); - } - - $type = (int)(ord($this->buffer[self::BYTE_HEADER]) & self::BITFIELD_TYPE); - - if (!in_array($type, Protocol::$frameTypes)) { - throw new FrameException('Invalid payload type'); - } - - return $type; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Listener/HandshakeRequestListener.php b/core/lib/Wrench/Listener/HandshakeRequestListener.php deleted file mode 100755 index 9b5b841ee..000000000 --- a/core/lib/Wrench/Listener/HandshakeRequestListener.php +++ /dev/null @@ -1,19 +0,0 @@ -allowed = $allowed; - } - - /** - * Handshake request listener - * - * Closes the connection on handshake from an origin that isn't allowed - * - * @param Connection $connection - * @param string $path - * @param string $origin - * @param string $key - * @param array $extensions - */ - public function onHandshakeRequest(Connection $connection, $path, $origin, $key, $extensions) - { - if (!$this->isAllowed($origin)) { - $connection->close(new InvalidOriginException('Origin not allowed')); - } - } - - /** - * Whether the specified origin is allowed under this policy - * - * @param string $origin - * @return boolean - */ - public function isAllowed($origin) - { - $scheme = parse_url($origin, PHP_URL_SCHEME); - $host = parse_url($origin, PHP_URL_HOST) ?: $origin; - - foreach ($this->allowed as $allowed) { - $allowed_scheme = parse_url($allowed, PHP_URL_SCHEME); - - if ($allowed_scheme && $scheme != $allowed_scheme) { - continue; - } - - $allowed_host = parse_url($allowed, PHP_URL_HOST) ?: $allowed; - - if ($host != $allowed_host) { - continue; - } - - return true; - } - - return false; - } - - /** - * @param Server $server - */ - public function listen(Server $server) - { - $server->addListener( - Server::EVENT_HANDSHAKE_REQUEST, - array($this, 'onHandshakeRequest') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Listener/RateLimiter.php b/core/lib/Wrench/Listener/RateLimiter.php deleted file mode 100755 index 6ca0fe86e..000000000 --- a/core/lib/Wrench/Listener/RateLimiter.php +++ /dev/null @@ -1,230 +0,0 @@ - - */ - protected $ips = array(); - - /** - * Request tokens per IP address - * - * @var array> - */ - protected $requests = array(); - - /** - * Constructor - * - * @param array $options - */ - public function __construct(array $options = array()) - { - parent::__construct($options); - } - - /** - * @param array $options - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'connections' => 200, // Total - 'connections_per_ip' => 5, // At once - 'requests_per_minute' => 200 // Per connection - ), $options); - - parent::configure($options); - } - - /** - * @see Wrench\Listener.Listener::listen() - */ - public function listen(Server $server) - { - $this->server = $server; - - $server->addListener( - Server::EVENT_SOCKET_CONNECT, - array($this, 'onSocketConnect') - ); - - $server->addListener( - Server::EVENT_SOCKET_DISCONNECT, - array($this, 'onSocketDisconnect') - ); - - $server->addListener( - Server::EVENT_CLIENT_DATA, - array($this, 'onClientData') - ); - } - - /** - * Event listener - * - * @param resource $socket - * @param Connection $connection - */ - public function onSocketConnect($socket, $connection) - { - $this->checkConnections($connection); - $this->checkConnectionsPerIp($connection); - } - - /** - * Event listener - * - * @param resource $socket - * @param Connection $connection - */ - public function onSocketDisconnect($socket, $connection) - { - $this->releaseConnection($connection); - } - - /** - * Event listener - * - * @param resource $socket - * @param Connection $connection - */ - public function onClientData($socket, $connection) - { - $this->checkRequestsPerMinute($connection); - } - - /** - * Idempotent - * - * @param Connection $connection - */ - protected function checkConnections($connection) - { - $connections = $connection->getConnectionManager()->count(); - - if ($connections > $this->options['connections']) { - $this->limit($connection, 'Max connections'); - } - } - - /** - * NOT idempotent, call once per connection - * - * @param Connection $connection - */ - protected function checkConnectionsPerIp($connection) - { - $ip = $connection->getIp(); - - if (!$ip) { - $this->log('Cannot check connections per IP', 'warning'); - return; - } - - if (!isset($this->ips[$ip])) { - $this->ips[$ip] = 1; - } else { - $this->ips[$ip] = min( - $this->options['connections_per_ip'], - $this->ips[$ip] + 1 - ); - } - - if ($this->ips[$ip] > $this->options['connections_per_ip']) { - $this->limit($connection, 'Connections per IP'); - } - } - - /** - * NOT idempotent, call once per disconnection - * - * @param Connection $connection - */ - protected function releaseConnection($connection) - { - $ip = $connection->getIp(); - - if (!$ip) { - $this->log('Cannot release connection', 'warning'); - return; - } - - if (!isset($this->ips[$ip])) { - $this->ips[$ip] = 0; - } else { - $this->ips[$ip] = max(0, $this->ips[$ip] - 1); - } - - unset($this->requests[$connection->getId()]); - } - - /** - * NOT idempotent, call once per data - * - * @param Connection $connection - */ - protected function checkRequestsPerMinute($connection) - { - $id = $connection->getId(); - - if (!isset($this->requests[$id])) { - $this->requests[$id] = array(); - } - - // Add current token - $this->requests[$id][] = time(); - - // Expire old tokens - while (reset($this->requests[$id]) < time() - 60) { - array_shift($this->requests[$id]); - } - - if (count($this->requests[$id]) > $this->options['requests_per_minute']) { - $this->limit($connection, 'Requests per minute'); - } - } - - /** - * Limits the given connection - * - * @param Connection $connection - * @param string $limit Reason - */ - protected function limit($connection, $limit) - { - $this->log(sprintf( - 'Limiting connection %s: %s', - $connection->getIp(), - $limit - ), 'notice'); - - $connection->close(new RateLimiterException($limit)); - } - - /** - * Logger - * - * @param string $message - * @param string $priority - */ - public function log($message, $priority = 'info') - { - $this->server->log('RateLimiter: ' . $message, $priority); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Payload/HybiPayload.php b/core/lib/Wrench/Payload/HybiPayload.php deleted file mode 100755 index 92d4821ab..000000000 --- a/core/lib/Wrench/Payload/HybiPayload.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - protected $frames = array(); - - /** - * Gets the current frame for the payload - * - * @return mixed - */ - protected function getCurrentFrame() - { - if (empty($this->frames)) { - array_push($this->frames, $this->getFrame()); - } - return end($this->frames); - } - - /** - * Gets the frame into which data should be receieved - * - * @throws PayloadException - * @return Frame - */ - protected function getReceivingFrame() - { - $current = $this->getCurrentFrame(); - - if ($current->isComplete()) { - if ($current->isFinal()) { - throw new PayloadException('Payload cannot receieve data: it is already complete'); - } else { - $current = array_push($this->frames, $this->getFrame()); - } - } - - return $current; - } - - /** - * Get a frame object - * - * @return Frame - */ - abstract protected function getFrame(); - - /** - * Whether the payload is complete - * - * @return boolean - */ - public function isComplete() - { - return $this->getCurrentFrame()->isComplete() && $this->getCurrentFrame()->isFinal(); - } - - /** - * Encodes a payload - * - * @param string $data - * @param int $type - * @param boolean $masked - * @return Payload - * @todo No splitting into multiple frames just yet - */ - public function encode($data, $type = Protocol::TYPE_TEXT, $masked = false) - { - $this->frames = array(); - - $frame = $this->getFrame(); - array_push($this->frames, $frame); - - $frame->encode($data, $type, $masked); - - return $this; - } - - /** - * Gets the number of remaining bytes before this payload will be - * complete - * - * May return 0 (no more bytes required) or null (unknown number of bytes - * required). - * - * @return number|NULL - */ - public function getRemainingData() - { - if ($this->isComplete()) { - return 0; - } - - try { - if ($this->getCurrentFrame()->isFinal()) { - return $this->getCurrentFrame()->getRemainingData(); - } - } catch (FrameException $e) { - return null; - } - - return null; - } - - /** - * Whether this payload is waiting for more data - * - * @return boolean - */ - public function isWaitingForData() - { - return $this->getRemainingData() > 0; - } - - /** - * @param Socket $socket - * @return boolean - */ - public function sendToSocket(Socket $socket) - { - $success = true; - foreach ($this->frames as $frame) { - $success = $success && ($socket->send($frame->getFrameBuffer()) !== false); - } - return $success; - } - - /** - * Receive raw data into the payload - * - * @param string $data - * @return void - */ - public function receiveData($data) - { - while ($data) { - $frame = $this->getReceivingFrame(); - - $size = strlen($data); - $remaining = $frame->getRemainingData(); - - if ($remaining === null) { - $chunk_size = 2; - } elseif ($remaining > 0) { - $chunk_size = $remaining; - } - - $chunk_size = min(strlen($data), $chunk_size); - $chunk = substr($data, 0, $chunk_size); - $data = substr($data, $chunk_size); - - $frame->receiveData($chunk); - } - } - - /** - * @return string - */ - public function getPayload() - { - $this->buffer = ''; - - foreach ($this->frames as $frame) { - $this->buffer .= $frame->getFramePayload(); - } - - return $this->buffer; - } - - /** - * @return string - */ - public function __toString() - { - try { - return $this->getPayload(); - } catch (\Exception $e) { - // __toString must not throw an exception - return ''; - } - } - - /** - * Gets the type of the payload - * - * The type of a payload is taken from its first frame - * - * @throws PayloadException - * @return int - */ - public function getType() - { - if (!isset($this->frames[0])) { - throw new PayloadException('Cannot tell payload type yet'); - } - return $this->frames[0]->getType(); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Payload/PayloadHandler.php b/core/lib/Wrench/Payload/PayloadHandler.php deleted file mode 100755 index 2be036ff1..000000000 --- a/core/lib/Wrench/Payload/PayloadHandler.php +++ /dev/null @@ -1,110 +0,0 @@ -callback = $callback; - } - - /** - * Handles the raw socket data given - * - * @param string $data - */ - public function handle($data) - { - if (!$this->payload) { - $this->payload = $this->protocol->getPayload(); - } - - while ($data) { // Each iteration pulls off a single payload chunk - $size = strlen($data); - $remaining = $this->payload->getRemainingData(); - - // If we don't yet know how much data is remaining, read data into - // the payload in two byte chunks (the size of a WebSocket frame - // header to get the initial length) - // - // Then re-loop. For extended lengths, this will happen once or four - // times extra, as the extended length is read in. - if ($remaining === null) { - $chunk_size = 2; - } elseif ($remaining > 0) { - $chunk_size = $remaining; - } elseif ($remaining === 0) { - $chunk_size = 0; - } - - $chunk_size = min(strlen($data), $chunk_size); - $chunk = substr($data, 0, $chunk_size); - $data = substr($data, $chunk_size); - - $this->payload->receiveData($chunk); - - if ($remaining !== 0 && !$this->payload->isComplete()) { - continue; - } - - if ($this->payload->isComplete()) { - $this->emit($this->payload); - $this->payload = $this->protocol->getPayload(); - } else { - throw new PayloadException('Payload will not complete'); - } - } - } - - /** - * Get the current payload - * - * @return Payload - */ - public function getCurrent() - { - return $this->getPayloadHandler->getCurrent(); - } - - /** - * Emits a complete payload to the callback - * - * @param Payload $payload - */ - protected function emit(Payload $payload) - { - call_user_func($this->callback, $payload); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Protocol/Hybi10Protocol.php b/core/lib/Wrench/Protocol/Hybi10Protocol.php deleted file mode 100755 index 33cfe15e3..000000000 --- a/core/lib/Wrench/Protocol/Hybi10Protocol.php +++ /dev/null @@ -1,35 +0,0 @@ -= 10) { - return true; - } - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Protocol/HybiProtocol.php b/core/lib/Wrench/Protocol/HybiProtocol.php deleted file mode 100755 index 1d73c2fab..000000000 --- a/core/lib/Wrench/Protocol/HybiProtocol.php +++ /dev/null @@ -1,24 +0,0 @@ - - */ - protected static $schemes = array( - self::SCHEME_WEBSOCKET, - self::SCHEME_WEBSOCKET_SECURE, - self::SCHEME_UNDERLYING, - self::SCHEME_UNDERLYING_SECURE - ); - - /** - * Close status codes - * - * @var array string> - */ - public static $closeReasons = array( - self::CLOSE_NORMAL => 'normal close', - self::CLOSE_GOING_AWAY => 'going away', - self::CLOSE_PROTOCOL_ERROR => 'protocol error', - self::CLOSE_DATA_INVALID => 'data invalid', - self::CLOSE_DATA_INCONSISTENT => 'data inconsistent', - self::CLOSE_POLICY_VIOLATION => 'policy violation', - self::CLOSE_MESSAGE_TOO_BIG => 'message too big', - self::CLOSE_EXTENSION_NEEDED => 'extension needed', - self::CLOSE_UNEXPECTED => 'unexpected error', - self::CLOSE_RESERVED => null, // Don't use these! - self::CLOSE_RESERVED_NONE => null, - self::CLOSE_RESERVED_ABNORM => null, - self::CLOSE_RESERVED_TLS => null - ); - - /** - * Frame types - * - * @todo flip values and keys? - * @var array int> - */ - public static $frameTypes = array( - 'continuation' => self::TYPE_CONTINUATION, - 'text' => self::TYPE_TEXT, - 'binary' => self::TYPE_BINARY, - 'close' => self::TYPE_CLOSE, - 'ping' => self::TYPE_PING, - 'pong' => self::TYPE_PONG - ); - - /** - * HTTP errors - * - * @var array string> - */ - public static $httpResponses = array( - self::HTTP_SWITCHING_PROTOCOLS => 'Switching Protocols', - self::HTTP_BAD_REQUEST => 'Bad Request', - self::HTTP_UNAUTHORIZED => 'Unauthorized', - self::HTTP_FORBIDDEN => 'Forbidden', - self::HTTP_NOT_FOUND => 'Not Found', - self::HTTP_NOT_IMPLEMENTED => 'Not Implemented', - self::HTTP_RATE_LIMITED => 'Enhance Your Calm' - ); - - /** - * Gets a version number - * - * @return - */ - abstract public function getVersion(); - - /** - * Subclasses should implement this method and return a boolean to the given - * version string, as to whether they would like to accept requests from - * user agents that specify that version. - * - * @return boolean - */ - abstract public function acceptsVersion($version); - - /** - * Gets a payload instance, suitable for use in decoding/encoding protocol - * frames - * - * @return Payload - */ - abstract public function getPayload(); - - /** - * Generates a key suitable for use in the protocol - * - * This base implementation returns a 16-byte (128 bit) random key as a - * binary string. - * - * @return string - */ - public function generateKey() - { - if (extension_loaded('openssl')) { - $key = openssl_random_pseudo_bytes(16); - } else { - // SHA1 is 128 bit (= 16 bytes) - $key = sha1(spl_object_hash($this) . mt_rand(0, PHP_INT_MAX) . uniqid('', true), true); - } - - return base64_encode($key); - } - - /** - * Gets request handshake string - * - * The leading line from the client follows the Request-Line format. - * The leading line from the server follows the Status-Line format. The - * Request-Line and Status-Line productions are defined in [RFC2616]. - * - * An unordered set of header fields comes after the leading line in - * both cases. The meaning of these header fields is specified in - * Section 4 of this document. Additional header fields may also be - * present, such as cookies [RFC6265]. The format and parsing of - * headers is as defined in [RFC2616]. - * - * @param string $uri WebSocket URI, e.g. ws://example.org:8000/chat - * @param string $key 16 byte binary string key - * @param string $origin Origin of the request - * @return string - */ - public function getRequestHandshake( - $uri, - $key, - $origin, - array $headers = array() - ) { - if (!$uri || !$key || !$origin) { - throw new InvalidArgumentException('You must supply a URI, key and origin'); - } - - list($scheme, $host, $port, $path) = self::validateUri($uri); - - $handshake = array( - sprintf(self::REQUEST_LINE_FORMAT, $path) - ); - - $headers = array_merge( - $this->getDefaultRequestHeaders( - $host . ':' . $port, $key, $origin - ), - $headers - ); - - foreach ($headers as $name => $value) { - $handshake[] = sprintf(self::HEADER_LINE_FORMAT, $name, $value); - } - return implode($handshake, "\r\n") . "\r\n\r\n"; - } - - /** - * Gets a handshake response body - * - * @param string $key - * @param array $headers - */ - public function getResponseHandshake($key, array $headers = array()) - { - $headers = array_merge( - $this->getSuccessResponseHeaders( - $key - ), - $headers - ); - - return $this->getHttpResponse(self::HTTP_SWITCHING_PROTOCOLS, $headers); - } - - /** - * Gets a response to an error in the handshake - * - * @param int|Exception $e Exception or HTTP error - * @param array $headers - */ - public function getResponseError($e, array $headers = array()) - { - $code = false; - - if ($e instanceof Exception) { - $code = $e->getCode(); - } elseif (is_numeric($e)) { - $code = (int)$e; - } - - if (!$code || $code < 400 || $code > 599) { - $code = self::HTTP_SERVER_ERROR; - } - - return $this->getHttpResponse($code, $headers); - } - - /** - * Gets an HTTP response - * - * @param int $status - * @param array $headers - */ - protected function getHttpResponse($status, array $headers = array()) - { - if (array_key_exists($status, self::$httpResponses)) { - $response = self::$httpResponses[$status]; - } else { - $response = self::$httpResponses[self::HTTP_NOT_IMPLEMENTED]; - } - - $handshake = array( - sprintf(self::RESPONSE_LINE_FORMAT, $status, $response) - ); - - foreach ($headers as $name => $value) { - $handshake[] = sprintf(self::HEADER_LINE_FORMAT, $name, $value); - } - - return implode($handshake, "\r\n") . "\r\n\r\n"; - } - - /** - * @todo better header handling - * @todo throw exception - * @param unknown_type $response - * @param unknown_type $key - * @return boolean - */ - public function validateResponseHandshake($response, $key) - { - if (!$response) { - return false; - } - - $headers = $this->getHeaders($response); - - if (!isset($headers[self::HEADER_ACCEPT])) { - throw new HandshakeException('No accept header receieved on handshake response'); - } - - $accept = $headers[self::HEADER_ACCEPT]; - - if (!$accept) { - throw new HandshakeException('Invalid accept header'); - } - - $expected = $this->getAcceptValue($key); - - preg_match('#Sec-WebSocket-Accept:\s(.*)$#mU', $response, $matches); - $keyAccept = trim($matches[1]); - - return ($keyAccept === $this->getEncodedHash($key)) ? true : false; - } - - /** - * Gets an encoded hash for a key - * - * @param string $key - * @return string - */ - public function getEncodedHash($key) - { - return base64_encode(pack('H*', sha1($key . self::MAGIC_GUID))); - } - - /** - * Validates a request handshake - * - * @param string $request - * @throws BadRequestException - */ - public function validateRequestHandshake( - $request - ) { - if (!$request) { - return false; - } - - list($request, $headers) = $this->getRequestHeaders($request); - // make a copy of the headers array to store all extra headers - $extraHeaders = $headers; - - // parse the resulting url to separate query parameters from the path - $url = parse_url($this->validateRequestLine($request)); - $path = isset($url['path']) ? $url['path'] : null; - $urlParams = array(); - if (isset($url['query'])) { - parse_str($url['query'], $urlParams); - } - - if (empty($headers[self::HEADER_ORIGIN])) { - throw new BadRequestException('No origin header'); - } else { - unset($extraHeaders[self::HEADER_ORIGIN]); - } - - $origin = $headers[self::HEADER_ORIGIN]; - - if (!isset($headers[self::HEADER_UPGRADE]) - || strtolower($headers[self::HEADER_UPGRADE]) != self::UPGRADE_VALUE - ) { - throw new BadRequestException('Invalid upgrade header'); - } else { - unset($extraHeaders[self::HEADER_UPGRADE]); - } - - if (!isset($headers[self::HEADER_CONNECTION]) - || stripos($headers[self::HEADER_CONNECTION], self::CONNECTION_VALUE) === false - ) { - throw new BadRequestException('Invalid connection header'); - } else { - unset($extraHeaders[self::HEADER_CONNECTION]); - } - - if (!isset($headers[self::HEADER_HOST])) { - // @todo Validate host == listening socket? Or would that break - // TCP proxies? - throw new BadRequestException('No host header'); - } else { - unset($extraHeaders[self::HEADER_HOST]); - } - - if (!isset($headers[self::HEADER_VERSION])) { - throw new BadRequestException('No version header received on handshake request'); - } - - if (!$this->acceptsVersion($headers[self::HEADER_VERSION])) { - throw new BadRequestException('Unsupported version: ' . $version); - } else { - unset($extraHeaders[self::HEADER_VERSION]); - } - - if (!isset($headers[self::HEADER_KEY])) { - throw new BadRequestException('No key header received'); - } - - $key = trim($headers[self::HEADER_KEY]); - - if (!$key) { - throw new BadRequestException('Invalid key'); - } else { - unset($extraHeaders[self::HEADER_KEY]); - } - - // Optional - $protocol = null; - if (isset($headers[self::HEADER_PROTOCOL])) { - $protocol = $headers[self::HEADER_PROTOCOL]; - unset($extraHeaders[self::HEADER_PROTOCOL]); - } - - $extensions = array(); - if (!empty($headers[self::HEADER_EXTENSIONS])) { - $extensions = $headers[self::HEADER_EXTENSIONS]; - if (is_scalar($extensions)) { - $extensions = array($extensions); - } - } - unset($extraHeaders[self::HEADER_EXTENSIONS]); - - return array($path, $origin, $key, $extensions, $protocol, $extraHeaders, $urlParams); - } - - /** - * Gets a suitable WebSocket close frame - * - * @param Exception|int $e - */ - public function getCloseFrame($e) - { - $code = false; - - if ($e instanceof Exception) { - $code = $e->getCode(); - } elseif (is_numeric($e)) { - $code = (int)$e; - } - - if (!$code || !key_exists($code, self::$closeReasons)) { - $code = self::CLOSE_UNEXPECTED; - } - - $body = pack('n', $code) . self::$closeReasons[$code]; - - $payload = $this->getPayload(); - return $payload->encode($body, self::TYPE_CLOSE); - } - - /** - * Validates a WebSocket URI - * - * @param string $uri - * @return array(string $scheme, string $host, int $port, string $path) - */ - public function validateUri($uri) - { - $uri = (string)$uri; - if (!$uri) { - throw new InvalidArgumentException('Invalid URI'); - } - - $scheme = parse_url($uri, PHP_URL_SCHEME); - $this->validateScheme($scheme); - - $host = parse_url($uri, PHP_URL_HOST); - if (!$host) { - throw new InvalidArgumentException("Invalid host"); - } - - $port = parse_url($uri, PHP_URL_PORT); - if (!$port) { - $port = $this->getPort($scheme); - } - - $path = parse_url($uri, PHP_URL_PATH); - if (!$path) { - throw new InvalidArgumentException('Invalid path'); - } - - return array($scheme, $host, $port, $path); - } - - /** - * Validates a socket URI - * - * @param string $uri - * @throws InvalidArgumentException - * @return array(string $scheme, string $host, string $port) - */ - public function validateSocketUri($uri) - { - $uri = (string)$uri; - if (!$uri) { - throw new InvalidArgumentException('Invalid URI'); - } - - $scheme = parse_url($uri, PHP_URL_SCHEME); - $scheme = $this->validateScheme($scheme); - - $host = parse_url($uri, PHP_URL_HOST); - if (!$host) { - throw new InvalidArgumentException("Invalid host"); - } - - $port = parse_url($uri, PHP_URL_PORT); - if (!$port) { - $port = $this->getPort($scheme); - } - - return array($scheme, $host, $port); - } - - /** - * Validates an origin URI - * - * @param string $origin - * @throws InvalidArgumentException - * @return string - */ - public function validateOriginUri($origin) - { - $origin = (string)$origin; - if (!$origin) { - throw new InvalidArgumentException('Invalid URI'); - } - - $scheme = parse_url($origin, PHP_URL_SCHEME); - if (!$scheme) { - throw new InvalidArgumentException('Invalid scheme'); - } - - $host = parse_url($origin, PHP_URL_HOST); - if (!$host) { - throw new InvalidArgumentException("Invalid host"); - } - - return $origin; - } - - /** - * Validates a request line - * - * @param string $line - * @throws BadRequestException - */ - protected function validateRequestLine($line) - { - $matches = array(0 => null, 1 => null); - - if (!preg_match(self::REQUEST_LINE_REGEX, $line, $matches) || !$matches[1]) { - throw new BadRequestException('Invalid request line', 400); - } - - return $matches[1]; - } - - /** - * Gets the expected accept value for a handshake response - * - * Note that the protocol calls for the base64 encoded value to be hashed, - * not the original 16 byte random key. - * - * @see http://tools.ietf.org/html/rfc6455#section-4.2.2 - * @param string $key - */ - protected function getAcceptValue($encoded_key) - { - return base64_encode(sha1($encoded_key . self::MAGIC_GUID, true)); - } - - /** - * Gets the headers from a full response - * - * @param string $response - * @return array() - * @throws InvalidArgumentException - */ - protected function getHeaders($response, &$request_line = null) - { - $parts = explode("\r\n\r\n", $response, 2); - - if (count($parts) != 2) { - $parts = array($parts, ''); - } - - list($headers, $body) = $parts; - - $return = array(); - foreach (explode("\r\n", $headers) as $header) { - $parts = explode(': ', $header, 2); - if (count($parts) == 2) { - list($name, $value) = $parts; - if (!isset($return[$name])) { - $return[$name] = $value; - } else { - if (is_array($return[$name])) { - $return[$name][] = $value; - } else { - $return[$name] = array($return[$name], $value); - } - } - } - } - - return $return; - } - - /** - * Gets request headers - * - * @param string $response - * @return array> The request line, and an array of - * headers - * @throws InvalidArgumentException - */ - protected function getRequestHeaders($response) - { - $eol = stripos($response, "\r\n"); - - if ($eol === false) { - throw new InvalidArgumentException('Invalid request line'); - } - - $request = substr($response, 0, $eol); - $headers = $this->getHeaders(substr($response, $eol + 2)); - - return array($request, $headers); - } - - /** - * Validates a scheme - * - * @param string $scheme - * @return string Underlying scheme - * @throws InvalidArgumentException - */ - protected function validateScheme($scheme) - { - if (!$scheme) { - throw new InvalidArgumentException('No scheme specified'); - } - if (!in_array($scheme, self::$schemes)) { - throw new InvalidArgumentException( - 'Unknown socket scheme: ' . $scheme - ); - } - - if ($scheme == self::SCHEME_WEBSOCKET_SECURE) { - return self::SCHEME_UNDERLYING_SECURE; - } - return self::SCHEME_UNDERLYING; - } - - /** - * Gets the default request headers - * - * @param string $host - * @param string $key - * @param string $origin - * @param int $version - * @return multitype:unknown string NULL - */ - protected function getDefaultRequestHeaders($host, $key, $origin) - { - return array( - self::HEADER_HOST => $host, - self::HEADER_UPGRADE => self::UPGRADE_VALUE, - self::HEADER_CONNECTION => self::CONNECTION_VALUE, - self::HEADER_KEY => $key, - self::HEADER_ORIGIN => $origin, - self::HEADER_VERSION => $this->getVersion() - ); - } - - /** - * Gets the default response headers - * - * @param string $key - */ - protected function getSuccessResponseHeaders($key) - { - return array( - self::HEADER_UPGRADE => self::UPGRADE_VALUE, - self::HEADER_CONNECTION => self::CONNECTION_VALUE, - self::HEADER_ACCEPT => $this->getAcceptValue($key) - ); - } - - /** - * Gets the default port for a scheme - * - * By default, the WebSocket Protocol uses port 80 for regular WebSocket - * connections and port 443 for WebSocket connections tunneled over - * Transport Layer Security - * - * @param string $uri - * @return int - */ - protected function getPort($scheme) - { - if ($scheme == self::SCHEME_WEBSOCKET) { - return 80; - } elseif ($scheme == self::SCHEME_WEBSOCKET_SECURE) { - return 443; - } elseif ($scheme == self::SCHEME_UNDERLYING) { - return 80; - } elseif ($scheme == self::SCHEME_UNDERLYING_SECURE) { - return 443; - } else { - throw new InvalidArgumentException('Unknown websocket scheme'); - } - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Protocol/Rfc6455Protocol.php b/core/lib/Wrench/Protocol/Rfc6455Protocol.php deleted file mode 100755 index b0f5c5289..000000000 --- a/core/lib/Wrench/Protocol/Rfc6455Protocol.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @author Simon Samtleben - * @author Dominic Scheirlinck - */ -class Server extends Configurable -{ - /**#@+ - * Events - * - * @var string - */ - const EVENT_SOCKET_CONNECT = 'socket_connect'; - const EVENT_SOCKET_DISCONNECT = 'socket_disconnect'; - const EVENT_HANDSHAKE_REQUEST = 'handshake_request'; - const EVENT_HANDSHAKE_SUCCESSFUL = 'handshake_successful'; - const EVENT_CLIENT_DATA = 'client_data'; - /**#@-*/ - - /** - * The URI of the server - * - * @var string - */ - protected $uri; - - /** - * Options - * - * @var array - */ - protected $options = array(); - - /** - * A logging callback - * - * The default callback simply prints to stdout. You can pass your own logger - * in the options array. It should take a string message and string priority - * as parameters. - * - * @var Closure - */ - protected $logger; - - /** - * Event listeners - * - * Add listeners using the addListener() method. - * - * @var array array> - */ - protected $listeners = array(); - - /** - * Connection manager - * - * @var ConnectionManager - */ - protected $connectionManager; - - /** - * Applications - * - * @var array Application> - */ - protected $applications = array(); - - /** - * Constructor - * - * @param string $uri Websocket URI, e.g. ws://localhost:8000/, path will - * be ignored - * @param array $options (optional) See configure - */ - public function __construct($uri, array $options = array()) - { - $this->uri = $uri; - - parent::__construct($options); - - $this->log('Server initialized', 'info'); - } - - /** - * Configure options - * - * Options include - * - socket_class => The socket class to use, defaults to ServerSocket - * - socket_options => An array of socket options - * - logger => Closure($message, $priority = 'info'), used - * for logging - * - * @param array $options - * @return void - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'connection_manager_class' => 'Wrench\ConnectionManager', - 'connection_manager_options' => array() - ), $options); - - parent::configure($options); - - $this->configureConnectionManager(); - $this->configureLogger(); - } - - /** - * Configures the logger - * - * @return void - */ - protected function configureLogger() - { - // Default logger - if (!isset($this->options['logger'])) { - $this->options['logger'] = function ($message, $priority = 'info') { - printf("%s: %s%s", $priority, $message, PHP_EOL); - }; - } - $this->setLogger($this->options['logger']); - } - - /** - * Configures the connection manager - * - * @return void - */ - protected function configureConnectionManager() - { - $class = $this->options['connection_manager_class']; - $options = $this->options['connection_manager_options']; - $this->connectionManager = new $class($this, $options); - } - - /** - * Gets the connection manager - * - * @return \Wrench\ConnectionManager - */ - public function getConnectionManager() - { - return $this->connectionManager; - } - - /** - * @return string - */ - public function getUri() - { - return $this->uri; - } - - /** - * Sets a logger - * - * @param Closure $logger - * @return void - */ - public function setLogger($logger) - { - if (!is_callable($logger)) { - throw new \InvalidArgumentException('Logger must be callable'); - } - $this->logger = $logger; - } - - /** - * Main server loop - * - * @return void This method does not return! - */ - public function run() - { - $this->connectionManager->listen(); - - while (true) { - /* - * If there's nothing changed on any of the sockets, the server - * will sleep and other processes will have a change to run. Control - * this behaviour with the timeout options. - */ - $this->connectionManager->selectAndProcess(); - - /* - * If the application wants to perform periodic operations or queries and push updates to clients based on the result then that logic can be implemented in the 'onUpdate' method. - */ - foreach($this->applications as $application) { - if(method_exists($application, 'onUpdate')) { - $application->onUpdate(); - } - } - } - } - - /** - * Logs a message to the server log - * - * The default logger simply prints the message to stdout. You can provide - * a logging closure. This is useful, for instance, if you've daemonized - * and closed STDOUT. - * - * @param string $message Message to display. - * @param string $type Type of message. - * @return void - */ - public function log($message, $priority = 'info') - { - call_user_func($this->logger, $message, $priority); - } - - /** - * Notifies listeners of an event - * - * @param string $event - * @param array $arguments Event arguments - * @return void - */ - public function notify($event, array $arguments = array()) - { - if (!isset($this->listeners[$event])) { - return; - } - - foreach ($this->listeners[$event] as $listener) { - call_user_func_array($listener, $arguments); - } - } - - /** - * Adds a listener - * - * Provide an event (see the Server::EVENT_* constants) and a callback - * closure. Some arguments may be provided to your callback, such as the - * connection the caused the event. - * - * @param string $event - * @param Closure $callback - * @return void - * @throws InvalidArgumentException - */ - public function addListener($event, $callback) - { - if (!isset($this->listeners[$event])) { - $this->listeners[$event] = array(); - } - - if (!is_callable($callback)) { - throw new InvalidArgumentException('Invalid listener'); - } - - $this->listeners[$event][] = $callback; - } - - /** - * Returns a server application. - * - * @param string $key Name of application. - * @return Application The application object. - */ - public function getApplication($key) - { - if (empty($key)) { - return false; - } - - if (array_key_exists($key, $this->applications)) { - return $this->applications[$key]; - } - - return false; - } - - /** - * Adds a new application object to the application storage. - * - * @param string $key Name of application. - * @param object $application The application object - * @return void - */ - public function registerApplication($key, $application) - { - $this->applications[$key] = $application; - } -} diff --git a/core/lib/Wrench/Socket/ClientSocket.php b/core/lib/Wrench/Socket/ClientSocket.php deleted file mode 100755 index 9f5fbe293..000000000 --- a/core/lib/Wrench/Socket/ClientSocket.php +++ /dev/null @@ -1,105 +0,0 @@ - int, seconds, default 2 - */ -class ClientSocket extends UriSocket -{ - /** - * Default connection timeout - * - * @var int seconds - */ - const TIMEOUT_CONNECT = 2; - - /** - * @see Wrench\Socket.Socket::configure() - * Options include: - * - ssl_verify_peer => boolean, whether to perform peer verification - * of SSL certificate used - * - ssl_allow_self_signed => boolean, whether ssl_verify_peer allows - * self-signed certs - * - timeout_connect => int, seconds, default 2 - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'timeout_connect' => self::TIMEOUT_CONNECT, - 'ssl_verify_peer' => false, - 'ssl_allow_self_signed' => true - ), $options); - - parent::configure($options); - } - - /** - * Connects to the given socket - */ - public function connect() - { - if ($this->isConnected()) { - return true; - } - - $errno = null; - $errstr = null; - - $this->socket = stream_socket_client( - $this->getUri(), - $errno, - $errstr, - $this->options['timeout_connect'], - STREAM_CLIENT_CONNECT, - $this->getStreamContext() - ); - - if (!$this->socket) { - throw new \Wrench\Exception\ConnectionException(sprintf( - 'Could not connect to socket: %s (%d)', - $errstr, - $errno - )); - } - - stream_set_timeout($this->socket, $this->options['timeout_socket']); - - return ($this->connected = true); - } - - public function reconnect() - { - $this->disconnect(); - $this->connect(); - } - - /** - * @see Wrench\Socket.UriSocket::getSocketStreamContextOptions() - */ - protected function getSocketStreamContextOptions() - { - $options = array(); - return $options; - } - - /** - * @see Wrench\Socket.UriSocket::getSslStreamContextOptions() - */ - protected function getSslStreamContextOptions() - { - $options = array(); - - if ($this->options['ssl_verify_peer']) { - $options['verify_peer'] = true; - } - - if ($this->options['ssl_allow_self_signed']) { - $options['allow_self_signed'] = true; - } - - return $options; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Socket/ServerClientSocket.php b/core/lib/Wrench/Socket/ServerClientSocket.php deleted file mode 100755 index 61583f6cc..000000000 --- a/core/lib/Wrench/Socket/ServerClientSocket.php +++ /dev/null @@ -1,25 +0,0 @@ -connect() or whatnot. - * - * @param resource $accepted_socket - * @param array $options - */ - public function __construct($accepted_socket, array $options = array()) - { - parent::__construct($options); - - $this->socket = $accepted_socket; - $this->connected = (boolean)$accepted_socket; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Socket/ServerSocket.php b/core/lib/Wrench/Socket/ServerSocket.php deleted file mode 100755 index facb89f86..000000000 --- a/core/lib/Wrench/Socket/ServerSocket.php +++ /dev/null @@ -1,126 +0,0 @@ - int, used to limit the number of outstanding - * connections in the socket's listen queue - * - ssl_cert_file => string, server SSL certificate - * file location. File should contain - * certificate and private key - * - ssl_passphrase => string, passphrase for the key - * - timeout_accept => int, seconds, default 5 - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'backlog' => 50, - 'ssl_cert_file' => null, - 'ssl_passphrase' => null, - 'ssl_allow_self_signed' => false, - 'timeout_accept' => self::TIMEOUT_ACCEPT - ), $options); - - parent::configure($options); - } - - /** - * Listens - * - * @throws ConnectionException - */ - public function listen() - { - $this->socket = stream_socket_server( - $this->getUri(), - $errno, - $errstr, - STREAM_SERVER_BIND|STREAM_SERVER_LISTEN. - $this->getStreamContext() - ); - - if (!$this->socket) { - throw new ConnectionException(sprintf( - 'Could not listen on socket: %s (%d)', - $errstr, - $errno - )); - } - - $this->listening = true; - } - - /** - * Accepts a new connection on the socket - * - * @throws ConnectionException - * @return resource - */ - public function accept() - { - $new = stream_socket_accept( - $this->socket, - $this->options['timeout_accept'] - ); - - if (!$new) { - throw new ConnectionException(socket_strerror(socket_last_error($new))); - } - - return $new; - } - - /** - * @see Wrench\Socket.UriSocket::getSocketStreamContextOptions() - */ - protected function getSocketStreamContextOptions() - { - $options = array(); - - if ($this->options['backlog']) { - $options['backlog'] = $this->options['backlog']; - } - - return $options; - } - - /** - * @see Wrench\Socket.UriSocket::getSslStreamContextOptions() - */ - protected function getSslStreamContextOptions() - { - $options = array(); - - if ($this->options['server_ssl_cert_file']) { - $options['local_cert'] = $this->options['server_ssl_cert_file']; - if ($this->options['server_ssl_passphrase']) { - $options['passphrase'] = $this->options['server_ssl_passphrase']; - } - } - - return $options; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Socket/Socket.php b/core/lib/Wrench/Socket/Socket.php deleted file mode 100755 index 97f6b28cc..000000000 --- a/core/lib/Wrench/Socket/Socket.php +++ /dev/null @@ -1,322 +0,0 @@ - int, seconds, default 2 - * - timeout_socket => int, seconds, default 5 - * - * @param array $options - * @return void - */ - protected function configure(array $options) - { - $options = array_merge(array( - 'timeout_socket' => self::TIMEOUT_SOCKET, - ), $options); - - parent::configure($options); - } - - /** - * Gets the name of the socket - */ - protected function getName() - { - if (!isset($this->name) || !$this->name) { - $this->name = @stream_socket_get_name($this->socket, true); - } - return $this->name; - } - - /** - * Gets part of the name of the socket - * - * PHP seems to return IPV6 address/port combos like this: - * ::1:1234, where ::1 is the address and 1234 the port - * So, the part number here is either the last : delimited section (the port) - * or all the other sections (the whole initial part, the address). - * - * @param string $name (from $this->getName() usually) - * @param int<0, 1> $part - * @return string - * @throws SocketException - */ - public static function getNamePart($name, $part) - { - if (!$name) { - throw new InvalidArgumentException('Invalid name'); - } - - $parts = explode(':', $name); - - if (count($parts) < 2) { - throw new SocketException('Could not parse name parts: ' . $name); - } - - if ($part == self::NAME_PART_PORT) { - return end($parts); - } elseif ($part == self::NAME_PART_IP) { - return implode(':', array_slice($parts, 0, -1)); - } else { - throw new InvalidArgumentException('Invalid name part'); - } - - return null; - } - - /** - * Gets the IP address of the socket - * - * @return string - */ - public function getIp() - { - $name = $this->getName(); - - if ($name) { - return self::getNamePart($name, self::NAME_PART_IP); - } else { - throw new SocketException('Cannot get socket IP address'); - } - } - - /** - * Gets the port of the socket - * - * @return int - */ - public function getPort() - { - $name = $this->getName(); - - if ($name) { - return self::getNamePart($name, self::NAME_PART_PORT); - } else { - throw new SocketException('Cannot get socket IP address'); - } - } - - /** - * Get the last error that occurred on the socket - * - * @return int|string - */ - public function getLastError() - { - if ($this->isConnected() && $this->socket) { - $err = @socket_last_error($this->socket); - if ($err) { - $err = socket_strerror($err); - } - if (!$err) { - $err = 'Unknown error'; - } - return $err; - } else { - return 'Not connected'; - } - } - - /** - * Whether the socket is currently connected - * - * @return boolean - */ - public function isConnected() - { - return $this->connected; - } - - /** - * Disconnect the socket - * - * @return void - */ - public function disconnect() - { - if ($this->socket) { - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); - } - $this->socket = null; - $this->connected = false; - } - - /** - * @see Wrench.Resource::getResource() - */ - public function getResource() - { - return $this->socket; - } - - /** - * @see Wrench.Resource::getResourceId() - */ - public function getResourceId() - { - return (int)$this->socket; - } - - /** - * @param unknown_type $data - * @throws SocketException - * @return boolean|int The number of bytes sent or false on error - */ - public function send($data) - { - if (!$this->isConnected()) { - throw new SocketException('Socket is not connected'); - } - - $length = strlen($data); - - if ($length == 0) { - return 0; - } - - for ($i = $length; $i > 0; $i -= $written) { - $written = @fwrite($this->socket, substr($data, -1 * $i)); - - if ($written === false) { - return false; - } elseif ($written === 0) { - return false; - } - } - - return $length; - } - - /** - * Receive data from the socket - * - * @param int $length - * @return string - */ - public function receive($length = self::DEFAULT_RECEIVE_LENGTH) - { - $remaining = $length; - - $buffer = ''; - $metadata['unread_bytes'] = 0; - - do { - if (feof($this->socket)) { - return $buffer; - } - - $result = fread($this->socket, $length); - - if ($result === false) { - return $buffer; - } - - $buffer .= $result; - - if (feof($this->socket)) { - return $buffer; - } - - $continue = false; - - if ($this->firstRead == true && strlen($result) == 1) { - // Workaround Chrome behavior (still needed?) - $continue = true; - } - $this->firstRead = false; - - if (strlen($result) == $length) { - $continue = true; - } - - // Continue if more data to be read - $metadata = stream_get_meta_data($this->socket); - if ($metadata && isset($metadata['unread_bytes']) && $metadata['unread_bytes']) { - $continue = true; - $length = $metadata['unread_bytes']; - } - } while ($continue); - - return $buffer; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Socket/UriSocket.php b/core/lib/Wrench/Socket/UriSocket.php deleted file mode 100755 index 8e1cbc047..000000000 --- a/core/lib/Wrench/Socket/UriSocket.php +++ /dev/null @@ -1,118 +0,0 @@ - Wrench\Protocol object, latest protocol - * version used if not specified - * - timeout_socket => int, seconds, default 5 - * - server_ssl_cert_file => string, server SSL certificate - * file location. File should contain - * certificate and private key - * - server_ssl_passphrase => string, passphrase for the key - * - server_ssl_allow_self_signed => boolean, whether to allows self- - * signed certs - */ - public function __construct($uri, array $options = array()) - { - parent::__construct($options); - - list($this->scheme, $this->host, $this->port) - = $this->protocol->validateSocketUri($uri); - } - - /** - * Gets the canonical/normalized URI for this socket - * - * @return string - */ - protected function getUri() - { - return sprintf( - '%s://%s:%d', - $this->scheme, - $this->host, - $this->port - ); - } - - /** - * @todo DNS lookup? Override getIp()? - * @see Wrench\Socket.Socket::getName() - */ - protected function getName() - { - return sprintf('%s:%s', $this->host, $this->port); - } - - /** - * Gets the host name - */ - public function getHost() - { - return $this->host; - } - - /** - * @see Wrench\Socket.Socket::getPort() - */ - public function getPort() - { - return $this->port; - } - - /** - * Gets a stream context - */ - protected function getStreamContext($listen = false) - { - $options = array(); - - if ($this->scheme == Protocol::SCHEME_UNDERLYING_SECURE - || $this->scheme == Protocol::SCHEME_UNDERLYING) { - $options['socket'] = $this->getSocketStreamContextOptions(); - } - - if ($this->scheme == Protocol::SCHEME_UNDERLYING_SECURE) { - $options['ssl'] = $this->getSslStreamContextOptions(); - } - - return stream_context_create( - $options, - array() - ); - } - - /** - * Returns an array of socket stream context options - * - * See http://php.net/manual/en/context.socket.php - * - * @return array - */ - abstract protected function getSocketStreamContextOptions(); - - /** - * Returns an array of ssl stream context options - * - * See http://php.net/manual/en/context.ssl.php - * - * @return array - */ - abstract protected function getSslStreamContextOptions(); -} diff --git a/core/lib/Wrench/Tests/Application/EchoApplicationTest.php b/core/lib/Wrench/Tests/Application/EchoApplicationTest.php deleted file mode 100755 index bd32aeb33..000000000 --- a/core/lib/Wrench/Tests/Application/EchoApplicationTest.php +++ /dev/null @@ -1,57 +0,0 @@ -assertInstanceOfClass($this->getInstance()); - } - - /** - * @param unknown_type $payload - * @dataProvider getValidPayloads - */ - public function testOnData($payload) - { - $connection = $this->getMockBuilder('Wrench\Connection') - ->disableOriginalConstructor() - ->getMock(); - - $connection - ->expects($this->once()) - ->method('send') - ->with($this->equalTo($payload), $this->equalTo(Protocol::TYPE_TEXT)) - ->will($this->returnValue(true)); - - $this->getInstance()->onData($payload, $connection); - } - - /** - * Data provider - * - * @return array> - */ - public function getValidPayloads() - { - return array( - array('asdkllakdaowidoaw noaoinosdna nwodinado ndsnd aklndiownd'), - array(' ') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/BasicServerTest.php b/core/lib/Wrench/Tests/BasicServerTest.php deleted file mode 100755 index 20d004419..000000000 --- a/core/lib/Wrench/Tests/BasicServerTest.php +++ /dev/null @@ -1,119 +0,0 @@ -getInstance('ws://localhost:8000', array( - 'allowed_origins' => $allowed, - 'logger' => array($this, 'log') - )); - - $connection = $this->getMockBuilder('Wrench\Connection') - ->disableOriginalConstructor() - ->getMock(); - - $connection - ->expects($this->never()) - ->method('close') - ->will($this->returnValue(true)); - - $server->notify( - Server::EVENT_HANDSHAKE_REQUEST, - array($connection, '', $origin, '', array()) - ); - } - - /** - * @param array $allowed - * @param string $origin - * @dataProvider getInvalidOrigins - */ - public function testInvalidOriginPolicy(array $allowed, $origin) - { - $server = $this->getInstance('ws://localhost:8000', array( - 'allowed_origins' => $allowed, - 'logger' => array($this, 'log') - )); - - $connection = $this->getMockBuilder('Wrench\Connection') - ->disableOriginalConstructor() - ->getMock(); - - $connection - ->expects($this->once()) - ->method('close') - ->will($this->returnValue(true)); - - $server->notify( - Server::EVENT_HANDSHAKE_REQUEST, - array($connection, '', $origin, '', array()) - ); - } - - /** - * @see Wrench\Tests.ServerTest::getValidConstructorArguments() - */ - public function getValidConstructorArguments() - { - return array_merge(parent::getValidConstructorArguments(), array( - array( - 'ws://localhost:8000', - array('logger' => function () {}) - ) - )); - } - - /** - * Data provider - * - * @return array> - */ - public function getValidOrigins() - { - return array( - array(array('localhost'), 'localhost'), - array(array('somewhere.com'), 'somewhere.com'), - ); - } - - /** - * Data provider - * - * @return array> - */ - public function getInvalidOrigins() - { - return array( - array(array('localhost'), 'blah'), - array(array('somewhere.com'), 'somewhereelse.com'), - array(array('somewhere.com'), 'subdomain.somewhere.com') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/ClientTest.php b/core/lib/Wrench/Tests/ClientTest.php deleted file mode 100755 index 824774eef..000000000 --- a/core/lib/Wrench/Tests/ClientTest.php +++ /dev/null @@ -1,173 +0,0 @@ -assertInstanceOfClass( - $client = new Client( - 'ws://localhost/test', 'http://example.org/' - ), - 'ws:// scheme, default socket' - ); - - $this->assertInstanceOfClass( - $client = new Client( - 'ws://localhost/test', 'http://example.org/', - array('socket' => $this->getMockSocket()) - ), - 'ws:// scheme, socket specified' - ); - } - - /** - * Gets a mock socket - * - * @return Socket - */ - protected function getMockSocket() - { - return $this->getMock('Wrench\Socket\ClientSocket', array(), array('wss://localhost:8000')); - } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function testConstructorSocketUnspecified() - { - $w = new Client(); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorUriInvalid() - { - $w = new Client('invalid uri', 'http://www.example.com/'); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorUriEmpty() - { - $w = new Client(null, 'http://www.example.com/'); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorUriPathUnspecified() - { - $w = new Client('ws://localhost', 'http://www.example.com/'); - } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function testConstructorOriginUnspecified() - { - $w = new Client('ws://localhost'); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorOriginEmpty() - { - $w = new Client('wss://localhost', null); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorOriginInvalid() - { - $w = new Client('ws://localhost:8000', 'NOTAVALIDURI'); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSendInvalidType() - { - $client = new Client('ws://localhost/test', 'http://example.org/'); - $client->sendData('blah', 9999); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testSendInvalidTypeString() - { - $client = new Client('ws://localhost/test', 'http://example.org/'); - $client->sendData('blah', 'fooey'); - } - - public function testSend() - { - try { - $helper = new ServerTestHelper(); - $helper->setUp(); - - /* @var $instance Wrench\Client */ - $instance = $this->getInstance($helper->getEchoConnectionString(), 'http://www.example.com/send'); - $instance->addRequestHeader('X-Test', 'Custom Request Header'); - - $this->assertFalse($instance->receive(), 'Receive before connect'); - - $success = $instance->connect(); - $this->assertTrue($success, 'Client can connect to test server'); - $this->assertTrue($instance->isConnected()); - - $this->assertFalse($instance->connect(), 'Double connect'); - - $this->assertFalse((boolean)$instance->receive(), 'No data'); - - $bytes = $instance->sendData('foobar', 'text'); - $this->assertTrue($bytes >= 6, 'sent text frame'); - sleep(1); - - $bytes = $instance->sendData('baz', Protocol::TYPE_TEXT); - $this->assertTrue($bytes >= 3, 'sent text frame'); - sleep(1); - - $responses = $instance->receive(); - $this->assertTrue(is_array($responses)); - $this->assertCount(2, $responses); - $this->assertInstanceOf('Wrench\\Payload\\Payload', $responses[0]); - $this->assertInstanceOf('Wrench\\Payload\\Payload', $responses[1]); - - $instance->disconnect(); - - $this->assertFalse($instance->isConnected()); - } catch (\Exception $e) { - $helper->tearDown(); - throw $e; - } - - $helper->tearDown(); - } -} diff --git a/core/lib/Wrench/Tests/ConnectionManagerTest.php b/core/lib/Wrench/Tests/ConnectionManagerTest.php deleted file mode 100755 index ed3de0b03..000000000 --- a/core/lib/Wrench/Tests/ConnectionManagerTest.php +++ /dev/null @@ -1,101 +0,0 @@ -assertInstanceOfClass( - $instance = $this->getInstance( - $server, - $options - ), - 'Valid constructor arguments' - ); - } - - /** - * Tests the constructor - */ - public function testConstructor() - { - $this->assertInstanceOfClass( - $instance = $this->getInstance( - $this->getMockServer(), - array() - ), - 'Constructor' - ); - return $instance; - } - - /** - * @depends testConstructor - * @param ConnectionManager $instance - */ - public function testCount($instance) - { - $this->assertTrue(is_numeric($instance->count())); - } - - /** - * Data provider - */ - public function getValidConstructorArguments() - { - return array( - array($this->getMockServer(), array()) - ); - } - - /** - * Gets a mock server - */ - protected function getMockServer() - { - $server = $this->getMock('Wrench\Server', array(), array(), '', false); - - $server->registerApplication('/echo', $this->getMockApplication()); - - $server->expects($this->any()) - ->method('getUri') - ->will($this->returnValue('ws://localhost:8000/')); - - return $server; - } - - /** - * Gets a mock application - * - * @return EchoApplication - */ - protected function getMockApplication() - { - return new EchoApplication(); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/ConnectionTest.php b/core/lib/Wrench/Tests/ConnectionTest.php deleted file mode 100755 index 25d57ffbc..000000000 --- a/core/lib/Wrench/Tests/ConnectionTest.php +++ /dev/null @@ -1,386 +0,0 @@ -assertInstanceOfClass( - $instance = $this->getInstance( - $manager, - $socket, - $options - ), - 'Valid constructor arguments' - ); - - return $instance; - } - - /** - * @dataProvider getValidCloseCodes - */ - public function testClose($code) - { - $socket = $this->getMockSocket(); - - $socket->expects($this->any()) - ->method('getIp') - ->will($this->returnValue('127.0.0.1')); - - $socket->expects($this->any()) - ->method('getPort') - ->will($this->returnValue(mt_rand(1025, 50000))); - - $manager = $this->getMockConnectionManager(); - - $connection = $this->getInstance($manager, $socket); - $connection->close($code); - } - - /** - * @dataProvider getValidHandshakeData - */ - public function testHandshake($path, $request) - { - $connection = $this->getConnectionForHandshake( - $this->getConnectedSocket(), - $path, - $request - ); - $connection->handshake($request); - $connection->onData('somedata'); - $this->assertTrue($connection->send('someotherdata')); - return $connection; - } - - /** - * @dataProvider getValidHandshakeData - * @expectedException Wrench\Exception\HandshakeException - */ - public function testHandshakeBadSocket($path, $request) - { - $connection = $this->getConnectionForHandshake( - $this->getNotConnectedSocket(), - $path, - $request - ); - $connection->handshake($request); - } - - /** - * Because expectation is that only $path application is available - * - * @dataProvider getWrongPathHandshakeData - * @expectedException PHPUnit_Framework_ExpectationFailedException - */ - public function testWrongPathHandshake($path, $request) - { - $connection = $this->getConnectionForHandshake( - $this->getConnectedSocket(), - $path, - $request - ); - $connection->handshake($request); - } - - /** - * @dataProvider getValidHandleData - */ - public function testHandle($path, $request_handshake, array $requests, array $counts) - { - $connection = $this->getConnectionForHandle( - $this->getConnectedSocket(), - $path, - $request_handshake, - $counts - ); - - $connection->handshake($request_handshake); - - foreach ($requests as $request) { - $connection->handle($request); - } - - return $connection; - } - - /** - * @return Socket - */ - protected function getConnectedSocket() - { - $socket = $this->getMockSocket(); - - $socket->expects($this->any()) - ->method('isConnected') - ->will($this->returnValue(true)); - - return $socket; - } - - /** - * @return Socket - */ - protected function getNotConnectedSocket() - { - $socket = $this->getMockSocket(); - - $socket->expects($this->any()) - ->method('isConnected') - ->will($this->returnValue(false)); - - return $socket; - } - - protected function getConnectionForHandshake($socket, $path, $request) - { - $manager = $this->getMockConnectionManager(); - - $application = $this->getMockApplication(); - - $server = $this->getMock('Wrench\Server', array(), array(), '', false); - $server->registerApplication($path, $application); - - $manager->expects($this->any()) - ->method('getApplicationForPath') - ->with($path) - ->will($this->returnValue($application)); - - $manager->expects($this->any()) - ->method('getServer') - ->will($this->returnValue($server)); - - $connection = $this->getInstance($manager, $socket); - - return $connection; - } - - protected function getConnectionForHandle($socket, $path, $handshake, array $counts) - { - $connection = $this->getConnectionForHandshake($socket, $path, $handshake); - - $manager = $this->getMockConnectionManager(); - - $application = $this->getMockApplication(); - - $application->expects($this->exactly(isset($counts['onData']) ? $counts['onData'] : 0)) - ->method('onData') - ->will($this->returnValue(true)); - - $server = $this->getMock('Wrench\Server', array(), array(), '', false); - $server->registerApplication($path, $application); - - $manager->expects($this->any()) - ->method('getApplicationForPath') - ->with($path) - ->will($this->returnValue($application)); - - $manager->expects($this->exactly(isset($counts['removeConnection']) ? $counts['removeConnection'] : 0)) - ->method('removeConnection'); - - $manager->expects($this->any()) - ->method('getServer') - ->will($this->returnValue($server)); - - $connection = $this->getInstance($manager, $socket); - - return $connection; - } - - /** - * @return ConnectionManager - */ - protected function getMockConnectionManager() - { - return $this->getMock('Wrench\ConnectionManager', array(), array(), '', false); - } - - /** - * Gets a mock socket - * - * @return Socket - */ - protected function getMockSocket() - { - return $this->getMock('Wrench\Socket\ServerClientSocket', array(), array(), '', false); - } - - /** - * Gets a mock application - * - * @return EchoApplication - */ - protected function getMockApplication() - { - return $this->getMock('Wrench\Application\EchoApplication'); - } - - /** - * Data provider - * - * @return array> - */ - public function getValidCloseCodes() - { - $arguments = array(); - foreach (Protocol::$closeReasons as $code => $reason) { - $arguments[] = array($code); - } - return $arguments; - } - - /** - * Data provider - * - * @return array> - */ - public function getValidConstructorArguments() - { - $socket = $this->getMockSocket(); - - $socket->expects($this->any()) - ->method('getIp') - ->will($this->returnValue('127.0.0.1')); - - $socket->expects($this->any()) - ->method('getPort') - ->will($this->returnValue(mt_rand(1025, 50000))); - - $manager = $this->getMockConnectionManager(); - - return array( - array( - $manager, - $socket, - array('logger' => function() {}) - ), - array( - $manager, - $socket, - array('logger' => function () {}, - 'connection_id_algo' => 'sha512') - ) - ); - } - - /** - * Data provider - * - * Uses this awkward valid request array so that splitting of payloads - * across multiple calls to handle can be tested - * - * testHandle($path, $request_handshake, array $requests, array $counts) - */ - public function getValidHandleData() - { - $valid_requests = array( - array( - 'data' => array( - "\x81\xad\x2e\xab\x82\xac\x6f\xfe\xd6\xe4\x14\x8b\xf9\x8c\x0c" - ."\xde\xf1\xc9\x5c\xc5\xe3\xc1\x4b\x89\xb8\x8c\x0c\xcd\xed\xc3" - ."\x0c\x87\xa2\x8e\x5e\xca\xf1\xdf\x59\xc4\xf0\xc8\x0c\x91\xa2" - ."\x8e\x4c\xca\xf0\x8e\x53\x81\xad\xd4\xfd\x81\xfe\x95\xa8\xd5" - ."\xb6\xee\xdd\xfa\xde\xf6\x88\xf2\x9b\xa6\x93\xe0\x93\xb1\xdf" - ."\xbb\xde\xf6\x9b\xee\x91\xf6\xd1\xa1\xdc\xa4\x9c\xf2\x8d\xa3" - ."\x92\xf3\x9a\xf6\xc7\xa1\xdc\xb6\x9c\xf3\xdc\xa9\x81\x80\x8e" - ."\x12\xcd\x8e\x81\x8c\xf6\x8a\xf0\xee\x9a\xeb\x83\x9a\xd6\xe7" - ."\x95\x9d\x85\xeb\x97\x8b" // Four text frames - ), - 'counts' => array( - 'onData' => 4 - ) - ), - array( - 'data' => array( - "\x88\x80\xdc\x8e\xa2\xc5" // Close frame - ), - 'counts' => array( - 'removeConnection' => 1 - ) - ) - ); - - $data = array(); - - $handshakes = $this->getValidHandshakeData(); - - foreach ($handshakes as $handshake) { - foreach ($valid_requests as $handle_args) { - $arguments = $handshake; - $arguments[] = $handle_args['data']; - $arguments[] = $handle_args['counts']; - - $data[] = $arguments; - } - } - - return $data; - } - - /** - * Data provider - */ - public function getValidHandshakeData() - { - return array( - array( - '/chat', -"GET /chat HTTP/1.1\r -Host: server.example.com\r -Upgrade: websocket\r -Connection: Upgrade\r -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r -Origin: http://example.com\r -Sec-WebSocket-Version: 13\r\n\r\n" - ) - ); - } - - /** - * Data provider - */ - public function getWrongPathHandshakeData() - { - return array( - array( - '/foobar', -"GET /chat HTTP/1.1\r -Host: server.example.com\r -Upgrade: websocket\r -Connection: Upgrade\r -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r -Origin: http://example.com\r -Sec-WebSocket-Version: 13\r\n\r\n" - ), - ); - } -} diff --git a/core/lib/Wrench/Tests/Frame/BaseSubclassFrameTest.php b/core/lib/Wrench/Tests/Frame/BaseSubclassFrameTest.php deleted file mode 100755 index bcced6695..000000000 --- a/core/lib/Wrench/Tests/Frame/BaseSubclassFrameTest.php +++ /dev/null @@ -1,29 +0,0 @@ -getFrameBuffer(); - } - - protected function getClass() - { - return 'Wrench\Tests\Frame\BadSubclassFrame'; - } -} diff --git a/core/lib/Wrench/Tests/Frame/FrameTest.php b/core/lib/Wrench/Tests/Frame/FrameTest.php deleted file mode 100755 index 848c7103e..000000000 --- a/core/lib/Wrench/Tests/Frame/FrameTest.php +++ /dev/null @@ -1,168 +0,0 @@ -frame = $this->getNewFrame(); - } - - protected function getNewFrame() - { - $class = $this->getClass(); - return new $class(); - } - - /** - * @see PHPUnit_Framework_TestCase::tearDown() - */ - protected function tearDown() - { - parent::tearDown(); - unset($this->frame); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testBijection($type, $payload, $masked) - { - // Encode the payload - $this->frame->encode($payload, $type, $masked); - - // Get the resulting buffer - $buffer = $this->frame->getFrameBuffer(); - $this->assertTrue((boolean)$buffer, 'Got raw frame buffer'); - - // And feed it back into a new frame - $frame = $this->getNewFrame(); - $frame->receiveData($buffer); - - // Check the properties of the new frame against the old, all match - $this->assertEquals( - $this->frame->getType(), - $frame->getType(), - 'Types match after encode -> receiveData' - ); - - $this->assertEquals( - $this->frame->getFramePayload(), - $frame->getFramePayload(), - 'Payloads match after encode -> receiveData' - ); - - // Masking key should not be different, because we read the buffer in directly - $this->assertEquals( - $this->frame->getFrameBuffer(), - $frame->getFrameBuffer(), - 'Raw buffers match too' - ); - - // This time, we create a new frame and read the data in with encode - $frame = $this->getNewFrame(); - $frame->encode($this->frame->getFramePayload(), $type, $masked); - - // These still match - $this->assertEquals( - $this->frame->getType(), - $frame->getType(), - 'Types match after encode -> receiveData -> encode' - ); - - $this->assertEquals( - $this->frame->getFramePayload(), - $frame->getFramePayload(), - 'Payloads match after encode -> receiveData -> encode' - ); - - // But the masking key should be different, thus, so are the buffers - if ($masked) { - $this->assertNotEquals( - $this->frame->getFrameBuffer(), - $frame->getFrameBuffer(), - 'Raw buffers don\'t match because of masking' - ); - } else { - $this->assertEquals( - $this->frame->getFramePayload(), - $frame->getFramePayload(), - 'Payloads match after encode -> receiveData -> encode' - ); - } - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testEncodeTypeReflection($type, $payload, $masked) - { - $this->frame->encode($payload, $type); - $this->assertEquals(Protocol::TYPE_TEXT, $this->frame->getType(), 'Encode retains type information'); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testEncodeLengthReflection($type, $payload, $masked) - { - $this->frame->encode($payload, $type); - $this->assertEquals(strlen($payload), $this->frame->getLength(), 'Encode does not alter payload length'); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testEncodePayloadReflection($type, $payload, $masked) - { - $this->frame->encode($payload, $type, $masked); - $this->assertEquals($payload, $this->frame->getFramePayload(), 'Encode retains payload information'); - } - - /** - * Data provider - * - * @return array - */ - public function getValidEncodePayloads() - { - return array( - array( - Protocol::TYPE_TEXT, - "123456\x007890!@#$%^&*()qwe\trtyuiopQWERTYUIOPasdfghjklASFGH\n - JKLzxcvbnmZXCVBNM,./<>?;[]{}-=_+\|'asdad0x11\aasdassasdasasdsd", - true - ), - array( - Protocol::TYPE_TEXT, - pack('CCCCCCC', 0x00, 0x01, 0x02, 0x03, 0x04, 0xff, 0xf0), - true - ), - array(Protocol::TYPE_TEXT, ' ', true) - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Frame/HybiFrameTest.php b/core/lib/Wrench/Tests/Frame/HybiFrameTest.php deleted file mode 100755 index 721429513..000000000 --- a/core/lib/Wrench/Tests/Frame/HybiFrameTest.php +++ /dev/null @@ -1,14 +0,0 @@ -getMock('Wrench\Server', array(), array(), '', false); - - $instance->listen($server); - } - - abstract public function testConstructor(); -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Listener/OriginPolicyTest.php b/core/lib/Wrench/Tests/Listener/OriginPolicyTest.php deleted file mode 100755 index 103af9bfe..000000000 --- a/core/lib/Wrench/Tests/Listener/OriginPolicyTest.php +++ /dev/null @@ -1,110 +0,0 @@ -getInstance(array()); - $this->assertInstanceOfClass($instance, 'No constructor arguments'); - return $instance; - } - - /** - * @dataProvider getValidArguments - * @param array $allowed - * @param string $domain - */ - public function testValidAllowed($allowed, $domain) - { - $instance = $this->getInstance($allowed); - $this->assertTrue($instance->isAllowed($domain)); - } - - /** - * @dataProvider getValidArguments - * @param array $allowed - * @param string $domain - */ - public function testValidHandshake($allowed, $domain) - { - $instance = $this->getInstance($allowed); - - $connection = $this->getMock('Wrench\Connection', array(), array(), '', false); - - $connection - ->expects($this->never()) - ->method('close'); - - $instance->onHandshakeRequest($connection, '/', $domain, 'abc', array()); - } - - /** - * @dataProvider getInvalidArguments - * @param array $allowed - * @param string $bad_domain - */ - public function testInvalidAllowed($allowed, $bad_domain) - { - $instance = $this->getInstance($allowed); - $this->assertFalse($instance->isAllowed($bad_domain)); - } - - /** - * @dataProvider getInvalidArguments - * @param array $allowed - * @param string $domain - */ - public function testInvalidHandshake($allowed, $bad_domain) - { - $instance = $this->getInstance($allowed); - - $connection = $this->getMock('Wrench\Connection', array(), array(), '', false); - - $connection - ->expects($this->once()) - ->method('close'); - - $instance->onHandshakeRequest($connection, '/', $bad_domain, 'abc', array()); - } - - /** - * Data provider - */ - public function getValidArguments() - { - return array( - array(array('localhost'), 'http://localhost'), - array(array('foobar.com'), 'https://foobar.com'), - array(array('https://foobar.com'), 'https://foobar.com') - ); - } - - /** - * Data provider - */ - public function getInvalidArguments() - { - return array( - array(array('localhost'), 'localdomain'), - array(array('foobar.com'), 'foobar.org'), - array(array('https://foobar.com'), 'http://foobar.com'), - array(array('http://foobar.com'), 'foobar.com') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Listener/RateLimiterTest.php b/core/lib/Wrench/Tests/Listener/RateLimiterTest.php deleted file mode 100755 index 602442308..000000000 --- a/core/lib/Wrench/Tests/Listener/RateLimiterTest.php +++ /dev/null @@ -1,67 +0,0 @@ -getInstance(); - $this->assertInstanceOfClass($instance, 'No constructor arguments'); - return $instance; - } - - public function testOnSocketConnect() - { - $this->getInstance()->onSocketConnect(null, $this->getConnection()); - } - - public function testOnSocketDisconnect() - { - $this->getInstance()->onSocketDisconnect(null, $this->getConnection()); - } - - public function testOnClientData() - { - $this->getInstance()->onClientData(null, $this->getConnection()); - } - - protected function getConnection() - { - $connection = $this->getMock('Wrench\Connection', array(), array(), '', false); - - $connection - ->expects($this->any()) - ->method('getIp') - ->will($this->returnValue('127.0.0.1')); - - $connection - ->expects($this->any()) - ->method('getId') - ->will($this->returnValue('abcdef01234567890')); - - $manager = $this->getMock('Wrench\ConnectionManager', array(), array(), '', false); - $manager->expects($this->any())->method('count')->will($this->returnValue(5)); - - $connection - ->expects($this->any()) - ->method('getConnectionManager') - ->will($this->returnValue($manager)); - - return $connection; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Payload/HybiPayloadTest.php b/core/lib/Wrench/Tests/Payload/HybiPayloadTest.php deleted file mode 100755 index 5dd2b2497..000000000 --- a/core/lib/Wrench/Tests/Payload/HybiPayloadTest.php +++ /dev/null @@ -1,14 +0,0 @@ -payload = $this->getInstance(); - } - - /** - * Tests the constructor - */ - public function testConstructor() - { - $this->assertInstanceOfClass($this->getInstance()); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testBijection($type, $payload) - { - // Encode the payload - $this->payload->encode($payload, $type); - - // Create a new payload and read the data in with encode - $payload = $this->getInstance(); - $payload->encode($this->payload->getPayload(), $type); - - // These still match - $this->assertEquals( - $this->payload->getType(), - $payload->getType(), - 'Types match after encode -> receiveData' - ); - - $this->assertEquals( - $this->payload->getPayload(), - $payload->getPayload(), - 'Payloads match after encode -> receiveData' - ); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testEncodeTypeReflection($type, $payload) - { - $this->payload->encode($payload, Protocol::TYPE_TEXT); - $this->assertEquals(Protocol::TYPE_TEXT, $this->payload->getType(), 'Encode retains type information'); - } - - /** - * @param string $payload - * @dataProvider getValidEncodePayloads - */ - public function testEncodePayloadReflection($type, $payload) - { - $this->payload->encode($payload, Protocol::TYPE_TEXT); - $this->assertEquals($payload, $this->payload->getPayload(), 'Encode retains payload information'); - } - - /** - * Tests sending to a socket - * @dataProvider getValidEncodePayloads - */ - public function testSendToSocket($type, $payload) - { - $successfulSocket = $this->getMock('Wrench\Socket\ClientSocket', array(), array('wss://localhost:8000')); - $failedSocket = clone $successfulSocket; - - $successfulSocket->expects($this->any()) - ->method('send') - ->will($this->returnValue(true)); - - $failedSocket->expects($this->any()) - ->method('send') - ->will($this->returnValue(false)); - - $this->payload->encode($payload, $type); - - $this->assertTrue($this->payload->sendToSocket($successfulSocket)); - $this->assertFalse($this->payload->sendToSocket($failedSocket)); - } - - /** - * Tests receiving data - * @dataProvider getValidEncodePayloads - */ - public function testReceieveData($type, $payload) - { - $payload = $this->getInstance(); - $payload->receiveData($payload); - } - - /** - * Data provider - * - * @return array - */ - public function getValidEncodePayloads() - { - return array( - array( - Protocol::TYPE_TEXT, - "123456\x007890!@#$%^&*()qwe\trtyuiopQWERTYUIOPasdfghjklASFGH\n - JKLzxcvbnmZXCVBNM,./<>?;[]{}-=_+\|'asdad0x11\aasdassasdasasdsd" - ), - array( - Protocol::TYPE_TEXT, - pack('CCCCCCC', 0x00, 0x01, 0x02, 0x03, 0x04, 0xff, 0xf0) - ), - array(Protocol::TYPE_TEXT, ' ') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Protocol/ProtocolTest.php b/core/lib/Wrench/Tests/Protocol/ProtocolTest.php deleted file mode 100755 index afba179da..000000000 --- a/core/lib/Wrench/Tests/Protocol/ProtocolTest.php +++ /dev/null @@ -1,180 +0,0 @@ -getInstance()->validateRequestHandshake($request); - - $this->assertEquals('/chat', $path); - $this->assertEquals('http://example.com', $origin); - $this->assertEquals('dGhlIHNhbXBsZSBub25jZQ==', $key); - $this->assertTrue(is_array($extensions), 'Extensions returned as array'); - $this->assertEquals(array('x-test', 'x-test2'), $extensions, 'Extensions match'); - $this->assertEquals('chat, superchat', $protocol); - } catch (Exception $e) { - $this->fail($e); - } - } - - /** - * @dataProvider getValidHandshakeResponses - */ - public function testValidateHandshakeResponseValid($response, $key) - { - try { - $valid = $this->getInstance()->validateResponseHandshake($response, $key); - $this->assertTrue(is_bool($valid), 'Validation return value is boolean'); - $this->assertTrue($valid, 'Handshake response validates'); - } catch (Exception $e) { - $this->fail('Validated valid response handshake as invalid'); - } - } - - /** - * @dataProvider getValidHandshakeResponses - */ - public function testGetResponseHandsake($unused, $key) - { - try { - $response = $this->getInstance()->getResponseHandshake($key); - $this->assertHttpResponse($response); - } catch (Exception $e) { - $this->fail('Unable to get handshake response: ' . $e); - } - } - - /** - * Asserts the string response is an HTTP response - * - * @param string $response - */ - protected function assertHttpResponse($response, $message = '') - { - $this->assertStringStartsWith('HTTP', $response, $message . ' - response starts well'); - $this->assertStringEndsWith("\r\n", $response, $message . ' - response ends well'); - } - - public function testGetVersion() - { - $version = $this->getInstance()->getVersion(); - $this->assertTrue(is_int($version)); - } - - public function testGetResponseError() - { - $response = $this->getInstance()->getResponseError(400); - $this->assertHttpResponse($response, 'Code as int'); - - $response = $this->getInstance()->getResponseError(new Exception('Some message', 500)); - $this->assertHttpResponse($response, 'Code in Exception'); - - $response = $this->getInstance()->getResponseError(888); - $this->assertHttpResponse($response, 'Invalid code produces unimplemented response'); - } - - /** - * @dataProvider getValidOriginUris - */ - public function testValidateOriginUriValid($uri) - { - try { - $this->getInstance()->validateOriginUri($uri); - } catch (\Exception $e) { - $this->fail('Valid URI validated as invalid: ' . $e); - } - } - - /** - * @dataProvider getInvalidOriginUris - * @expectedException InvalidArgumentException - */ - public function testValidateOriginUriInvalid($uri) - { - $this->getInstance()->validateOriginUri($uri); - } - - public function getValidOriginUris() - { - return array( - array('http://www.example.org'), - array('http://www.example.com/some/page'), - array('https://localhost/') - ); - } - - public function getInvalidOriginUris() - { - return array( - array(false), - array(true), - array(''), - array('blah') - ); - } - - public function getValidHandshakeRequests() - { - $cases = array(); - - - $cases[] = array("GET /chat HTTP/1.1\r -Host: server.example.com\r -Upgrade: websocket\r -Connection: Upgrade\r -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r -Origin: http://example.com\r -Sec-WebSocket-Extensions: x-test\r -Sec-WebSocket-Extensions: x-test2\r -Sec-WebSocket-Protocol: chat, superchat\r -Sec-WebSocket-Version: 13\r -\r\n"); - - $cases[] = array("GET /chat HTTP/1.1\r -Host: server.example.com\r -Upgrade: Websocket\r -Connection: Upgrade\r -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r -Origin: http://example.com\r -Sec-WebSocket-Extensions: x-test\r -Sec-WebSocket-Extensions: x-test2\r -Sec-WebSocket-Protocol: chat, superchat\r -Sec-WebSocket-Version: 13\r -\r\n"); - - return $cases; - } - - public function getValidHandshakeResponses() - { - $cases = array(); - - for ($i = 10; $i > 0; $i--) { - $key = sha1(time() . uniqid('', true)); - $response = "Sec-WebSocket-Accept: " - . base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)) - . "\r\n\r\n"; - - $cases[] = array($response, $key); - } - - return $cases; - } -} diff --git a/core/lib/Wrench/Tests/Protocol/Rfc6455ProtocolTest.php b/core/lib/Wrench/Tests/Protocol/Rfc6455ProtocolTest.php deleted file mode 100755 index ee329f31e..000000000 --- a/core/lib/Wrench/Tests/Protocol/Rfc6455ProtocolTest.php +++ /dev/null @@ -1,14 +0,0 @@ -assertInstanceOfClass( - $this->getInstance($url, $options), - 'Valid constructor arguments' - ); - } - - /** - * Tests logging - */ - public function testLogging() - { - $test = $this; - $logged = false; - - $server = $this->getInstance('ws://localhost:8000', array( - 'logger' => function ($message, $priority) use ($test, &$logged) { - $test->assertTrue(is_string($message), 'Log had a string message'); - $test->assertTrue(is_string($priority), 'Log had a string priority'); - $logged = true; - } - )); - - $this->assertTrue($logged, 'The log callback was hit'); - } - - /** - * Data provider - * - * @return array> - */ - public function getValidConstructorArguments() - { - return array( - array( - 'ws://localhost:8000', - array('logger' => array($this, 'log')) - ), - array( - 'ws://localhost', - array('logger' => array($this, 'log')) - ) - ); - } - -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/ServerTestHelper.php b/core/lib/Wrench/Tests/ServerTestHelper.php deleted file mode 100755 index 57dd1ec05..000000000 --- a/core/lib/Wrench/Tests/ServerTestHelper.php +++ /dev/null @@ -1,144 +0,0 @@ -tearDown(); - } - - /** - * @return string - */ - public function getConnectionString() - { - return 'ws://localhost:' . $this->port; - } - - /** - * @return string - */ - public function getEchoConnectionString() - { - return $this->getConnectionString() . '/echo'; - } - - /** - * Sets up the server process and sleeps for a few seconds while - * it wakes up - */ - public function setUp() - { - $this->port = self::getNextPort(); - - $this->process = proc_open( - $this->getCommand(), - array( - 0 => array('file', '/dev/null', 'r'), - 1 => array('file', __DIR__ . '/../../../build/server.log', 'a+'), - 2 => array('file', __DIR__ . '/../../../build/server.err.log', 'a+') - ), - $this->pipes, - __DIR__ . '../' - ); - - sleep(3); - } - - /** - * Tears down the server process - * - * This method *must* be called - */ - public function tearDown() - { - if ($this->process) { - foreach ($this->pipes as &$pipe) { - fclose($pipe); - } - $this->pipes = null; - - // Sigh - $status = proc_get_status($this->process); - - if ($status && isset($status['pid']) && $status['pid']) { - // More sigh, this is the pid of the parent sh process, we want - // to terminate the server directly - $this->log('Command: /bin/ps -ao pid,ppid | /usr/bin/col | /usr/bin/tail -n +2 | /bin/grep \' ' . $status['pid'] . "'", 'info'); - exec('/bin/ps -ao pid,ppid | /usr/bin/col | /usr/bin/tail -n +2 | /bin/grep \' ' . $status['pid'] . "'", $processes, $return); - - if ($return === 0) { - foreach ($processes as $process) { - list($pid, $ppid) = explode(' ', str_replace(' ', ' ', $process)); - if ($pid) { - $this->log('Killing ' . $pid, 'info'); - exec('/bin/kill ' . $pid . ' > /dev/null 2>&1'); - } - } - } else { - $this->log('Unable to find child processes', 'warning'); - } - - sleep(1); - - $this->log('Killing ' . $status['pid'], 'info'); - exec('/bin/kill ' . $status['pid'] . ' > /dev/null 2>&1'); - - sleep(1); - } - - proc_close($this->process); - $this->process = null; - } - } - - /** - * Gets the server command - * - * @return string - */ - protected function getCommand() - { - return sprintf('/usr/bin/env php %s/server.php %d', __DIR__, $this->port); - } - - /** - * Logs a message - * - * @param string $message - * @param string $priority - */ - public function log($message, $priority = 'info') - { - //echo $message . "\n"; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Socket/ClientSocketTest.php b/core/lib/Wrench/Tests/Socket/ClientSocketTest.php deleted file mode 100755 index d64ea5df2..000000000 --- a/core/lib/Wrench/Tests/Socket/ClientSocketTest.php +++ /dev/null @@ -1,167 +0,0 @@ -assertInstanceOfClass( - new ClientSocket('ws://localhost/'), - 'ws:// scheme, default port' - ); - - $this->assertInstanceOfClass( - new ClientSocket('ws://localhost/some-arbitrary-path'), - 'with path' - ); - - $this->assertInstanceOfClass( - new ClientSocket('wss://localhost/test', array()), - 'empty options' - ); - - $this->assertInstanceOfClass( - new ClientSocket('ws://localhost:8000/foo'), - 'specified port' - ); - - return $instance; - } - - public function testOptions() - { - $socket = null; - - $this->assertInstanceOfClass( - $socket = new ClientSocket( - 'ws://localhost:8000/foo', array( - 'timeout_connect' => 10 - ) - ), - 'connect timeout' - ); - - $this->assertInstanceOfClass( - $socket = new ClientSocket( - 'ws://localhost:8000/foo', array( - 'timeout_socket' => 10 - ) - ), - 'socket timeout' - ); - - $this->assertInstanceOfClass( - $socket = new ClientSocket( - 'ws://localhost:8000/foo', array( - 'protocol' => new Rfc6455Protocol() - ) - ), - 'protocol' - ); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testProtocolTypeError() - { - $socket = new ClientSocket( - 'ws://localhost:8000/foo', array( - 'protocol' => new stdClass() - ) - ); - } - - /** - * @expectedException PHPUnit_Framework_Error - */ - public function testConstructorUriUnspecified() - { - $w = new ClientSocket(); - } - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorUriEmpty() - { - $w = new ClientSocket(null); - } - - - /** - * @expectedException InvalidArgumentException - */ - public function testConstructorUriInvalid() - { - $w = new ClientSocket('Bad argument'); - } - - - /** - * @depends testConstructor - * @expectedException Wrench\Exception\SocketException - */ - public function testSendTooEarly($instance) - { - $instance->send('foo'); - } - - /** - * Test the connect, send, receive method - */ - public function testConnect() - { - try { - $helper = new ServerTestHelper(); - $helper->setUp(); - - $instance = $this->getInstance($helper->getConnectionString()); - $success = $instance->connect(); - - $this->assertTrue($success, 'Client socket can connect to test server'); - - $sent = $instance->send("GET /echo HTTP/1.1\r -Host: localhost\r -Upgrade: websocket\r -Connection: Upgrade\r -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r -Origin: http://localhost\r -Sec-WebSocket-Version: 13\r\n\r\n"); - $this->assertNotEquals(false, $sent, 'Client socket can send to test server'); - - $response = $instance->receive(); - $this->assertStringStartsWith('HTTP', $response, 'Response looks like HTTP handshake response'); - - } catch (\Exception $e) { - $helper->tearDown(); - throw $e; - } - - $helper->tearDown(); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Socket/ServerClientSocketTest.php b/core/lib/Wrench/Tests/Socket/ServerClientSocketTest.php deleted file mode 100755 index ca71983b5..000000000 --- a/core/lib/Wrench/Tests/Socket/ServerClientSocketTest.php +++ /dev/null @@ -1,42 +0,0 @@ -getInstance($resource); - $this->assertInstanceOfClass($instance); - return $instance; - } - - /** - * @expectedException Wrench\Exception\SocketException - * @depends testConstructor - */ - public function testGetIpTooSoon($instance) - { - $instance->getIp(); - } - - /** - * @expectedException Wrench\Exception\SocketException - * @depends testConstructor - */ - public function testGetPortTooSoon($instance) - { - $instance->getPort(); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Socket/ServerSocketTest.php b/core/lib/Wrench/Tests/Socket/ServerSocketTest.php deleted file mode 100755 index a4d4251c5..000000000 --- a/core/lib/Wrench/Tests/Socket/ServerSocketTest.php +++ /dev/null @@ -1,13 +0,0 @@ -isConnected(); - $this->assertTrue(is_bool($connected), 'isConnected returns boolean'); - $this->assertFalse($connected); - } - - /** - * @dataProvider getValidNames - * @param string $name - */ - public function testGetNamePart($name, $ip, $port) - { - $this->assertEquals($ip, Socket::getNamePart($name, Socket::NAME_PART_IP), 'splits ip correctly'); - $this->assertEquals($port, Socket::getNamePart($name, Socket::NAME_PART_PORT), 'splits port correctly'); - } - - /** - * Data provider - */ - public function getValidNames() - { - return array( - array('127.0.0.1:52339', '127.0.0.1', '52339'), - array('255.255.255.255:1025', '255.255.255.255', '1025'), - array('::1:56670', '::1', '56670') - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Socket/UriSocketTest.php b/core/lib/Wrench/Tests/Socket/UriSocketTest.php deleted file mode 100755 index d0b7f8bdc..000000000 --- a/core/lib/Wrench/Tests/Socket/UriSocketTest.php +++ /dev/null @@ -1,56 +0,0 @@ -getInstance('ws://localhost:8000'); - $this->assertInstanceOfClass($instance); - return $instance; - } - - /** - * @dataProvider getInvalidConstructorArguments - * @expectedException InvalidArgumentException - */ - public function testInvalidConstructor($uri) - { - $this->getInstance($uri); - } - - /** - * @depends testConstructor - */ - public function testGetIp($instance) - { - $this->assertStringStartsWith('localhost', $instance->getIp(), 'Correct host'); - } - - /** - * @depends testConstructor - */ - public function testGetPort($instance) - { - $this->assertEquals(8000, $instance->getPort(), 'Correct port'); - } - - /** - * Data provider - */ - public function getInvalidConstructorArguments() - { - return array( - array(false), - array('http://www.google.com/'), - array('ws:///'), - array(':::::'), - ); - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Tests/Test.php b/core/lib/Wrench/Tests/Test.php deleted file mode 100755 index f9b421fd9..000000000 --- a/core/lib/Wrench/Tests/Test.php +++ /dev/null @@ -1,61 +0,0 @@ -assertInstanceOf( - $this->getClass(), - $instance, - $message - ); - } - - /** - * Gets an instance of the class under test - * - * @param mixed Normal constructor arguments - * @magic This method accepts a variable number of arguments - * @return object Of type given by getClass() - */ - public function getInstance(/* ... */) - { - $reflection = new ReflectionClass($this->getClass()); - return $reflection->newInstanceArgs(func_get_args()); - } - - /** - * Logging function - * - * Passed into some classes under test as a callable - * - * @param string $message - * @param string $priority - * @return void - */ - public function log($message, $priority = 'info') - { - // nothing - } -} diff --git a/core/lib/Wrench/Tests/bootstrap.php b/core/lib/Wrench/Tests/bootstrap.php deleted file mode 100755 index 84bd83943..000000000 --- a/core/lib/Wrench/Tests/bootstrap.php +++ /dev/null @@ -1,11 +0,0 @@ -register(); \ No newline at end of file diff --git a/core/lib/Wrench/Tests/server.php b/core/lib/Wrench/Tests/server.php deleted file mode 100755 index 34d680622..000000000 --- a/core/lib/Wrench/Tests/server.php +++ /dev/null @@ -1,16 +0,0 @@ -register(); - -$server = new Wrench\Server('ws://localhost:' . $port); -$server->registerApplication('echo', new Wrench\Application\EchoApplication()); -$server->run(); \ No newline at end of file diff --git a/core/lib/Wrench/Util/Configurable.php b/core/lib/Wrench/Util/Configurable.php deleted file mode 100755 index f1983d5e6..000000000 --- a/core/lib/Wrench/Util/Configurable.php +++ /dev/null @@ -1,67 +0,0 @@ - Wrench\Protocol object, latest protocol - * version used if not specified - */ - public function __construct( - array $options = array() - ) { - $this->configure($options); - $this->configureProtocol(); - } - - /** - * Configures the options - * - * @param array $options - */ - protected function configure(array $options) - { - $this->options = array_merge(array( - 'protocol' => new Rfc6455Protocol() - ), $options); - } - - /** - * Configures the protocol option - * - * @throws InvalidArgumentException - */ - protected function configureProtocol() - { - $protocol = $this->options['protocol']; - - if (!$protocol || !($protocol instanceof Protocol)) { - throw new InvalidArgumentException('Invalid protocol option'); - } - - $this->protocol = $protocol; - } -} \ No newline at end of file diff --git a/core/lib/Wrench/Util/Ssl.php b/core/lib/Wrench/Util/Ssl.php deleted file mode 100755 index e8cc8bd7e..000000000 --- a/core/lib/Wrench/Util/Ssl.php +++ /dev/null @@ -1,51 +0,0 @@ - $country_name, - 'stateOrProvinceName' => $state_or_province_name, - 'localityName' => $locality_name, - 'organizationName' => $organization_name, - 'organizationalUnitName' => $organizational_unit_name, - 'commonName' => $common_name, - 'emailAddress' => $email_address - ); - - $privkey = openssl_pkey_new(); - $cert = openssl_csr_new($dn, $privkey); - $cert = openssl_csr_sign($cert, null, $privkey, 365); - - $pem = array(); - - openssl_x509_export($cert, $pem[0]); - - if ($pem_passphrase !== null) { - openssl_pkey_export($privkey, $pem[1], $pem_passphrase); - } - - $pem = implode($pem); - file_put_contents($pem_file, $pem); - } -} \ No newline at end of file diff --git a/core/lib/alchemy/zippy/.gitignore b/core/lib/alchemy/zippy/.gitignore deleted file mode 100644 index 357feac89..000000000 --- a/core/lib/alchemy/zippy/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -/tests/phpunit_report -/nbproject/ -/vendor/ -/docs/build -composer.phar - diff --git a/core/lib/alchemy/zippy/.travis.yml b/core/lib/alchemy/zippy/.travis.yml deleted file mode 100644 index 20cd2dcb1..000000000 --- a/core/lib/alchemy/zippy/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: php - -before_script: - - sudo apt-get install bsdtar zip - - composer install --dev --prefer-source - -env: - - ZIPPY_ADAPTER=ZipAdapter - - ZIPPY_ADAPTER=ZipExtensionAdapter - - ZIPPY_ADAPTER=GNUTar\\TarGNUTarAdapter - - ZIPPY_ADAPTER=GNUTar\\TarGzGNUTarAdapter - - ZIPPY_ADAPTER=GNUTar\\TarBz2GNUTarAdapter - - ZIPPY_ADAPTER=BSDTar\\TarBSDTarAdapter - - ZIPPY_ADAPTER=BSDTar\\TarGzBSDTarAdapter - - ZIPPY_ADAPTER=BSDTar\\TarBz2BSDTarAdapter - -php: - - 5.3.3 - - 5.4 - - 5.5 - -script: - - phpunit -v - - phpunit -v -c phpunit-functional.xml.dist diff --git a/core/lib/alchemy/zippy/CHANGELOG.md b/core/lib/alchemy/zippy/CHANGELOG.md deleted file mode 100644 index d54b9798c..000000000 --- a/core/lib/alchemy/zippy/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -CHANGELOG ---------- - -* 0.2.0 (04-04-2014) - - * Fix the use of "teleporter" for local files - * Fix adding a new file using tar adapter ( --append option ) - * Allow all adapters to be instantiated even if they are not supported - * Move support detection logic in distinct classes - * Add support for archives relative path - * Use Symfony Process working directory instead of changing working directory - * Archive in context when a single resource is added - -* 0.1.1 (04-12-2013) - - * Throw exception in case chdir failed - * Use guzzle stream download to handle large files without large memory usage - -* 0.1.0 (11-03-2013) - - * First stable version. - * Support for GNUtar, BSDtar, Zip, PHPZip. diff --git a/core/lib/alchemy/zippy/LICENSE b/core/lib/alchemy/zippy/LICENSE deleted file mode 100644 index b7247e120..000000000 --- a/core/lib/alchemy/zippy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Zippy is released under the MIT License : - -Copyright (c) 2012 Alchemy - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. diff --git a/core/lib/alchemy/zippy/README.md b/core/lib/alchemy/zippy/README.md deleted file mode 100644 index fe8b974e2..000000000 --- a/core/lib/alchemy/zippy/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# Zippy - -A Object-Oriented PHP library to manipulate any archive format (de)compression -through commandline utilities or PHP extension. - -[![Build Status](https://secure.travis-ci.org/alchemy-fr/Zippy.png?branch=master)](http://travis-ci.org/alchemy-fr/Zippy) - -## Adapters - -Zippy currently supports - - - zip - - PHP zip - - GNU tar - - BSD tar - -Which brings support to file types - - - .tar - - .zip - - .tar.gz - - .tar.bz2 - -## API Example - -### Archive listing and extraction : - -```php -use Alchemy\Zippy\Zippy; - -$zippy = Zippy::load(); -$zippy->create('archive.zip', '/path/to/folder'); - -$archive = $zippy->open('build.tar'); - -// extract content to `/tmp` -$archive->extract('/tmp'); - -// iterates through members -foreach ($archive as $member) { - echo "archive contains $member \n"; -} -``` - -### Archive creation - -```php -use Alchemy\Zippy\Zippy; - -$zippy = Zippy::load(); -// creates an archive.zip that contains a directory "folder" that contains -// files contained in "/path/to/directory" recursively -$archive = $zippy->create('archive.zip', array( - 'folder' => '/path/to/directory' -), recursive = true); -``` - -### Customize file and directory names inside archive - -```php -use Alchemy\Zippy\Zippy; - -$zippy = Zippy::load(); -$archive = $zippy->create('archive.zip', array( - 'folder' => '/path/to/directory', // will create a folder at root - 'http://www.google.com/logo.jpg', // will create a logo.jpg file at root - fopen('https://www.facebook.com/index.php'), // will create an index.php at root - 'directory/image.jpg' => 'image.jpg', // will create a image.jpg in 'directory' folder -)); -``` - -##API Browser - -## Documentation - -Documentation hosted at [read the docs](https://zippy.readthedocs.org/) ! - -##License - -This project is licensed under the [MIT license](http://opensource.org/licenses/MIT). diff --git a/core/lib/alchemy/zippy/composer.json b/core/lib/alchemy/zippy/composer.json deleted file mode 100644 index 2454aa93b..000000000 --- a/core/lib/alchemy/zippy/composer.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "alchemy/zippy", - "type": "library", - "description": "Zippy, the archive manager companion", - "keywords": ["zip", "tar", "bzip", "compression"], - "license": "MIT", - "authors": [ - { - "name": "Alchemy", - "email": "dev.team@alchemy.fr", - "homepage": "http://www.alchemy.fr/" - } - ], - "require": { - "php" : ">=5.3.3", - "doctrine/collections" : "~1.0", - "guzzle/guzzle" : "~3.0", - "pimple/pimple" : "~1.0", - "symfony/process" : "~2.0", - "symfony/filesystem" : "~2.0" - }, - "require-dev": { - "ext-zip" : "*", - "phpunit/phpunit" : "~3.7", - "symfony/finder" : "~2.0", - "sami/sami" : "dev-master@dev" - }, - "suggest": { - "ext-zip" : "To use the ZipExtensionAdapter" - }, - "autoload": { - "psr-0": { - "Alchemy": "src" - } - }, - "extra": { - "branch-alias": { - "dev-master": "0.2.x-dev" - } - } -} diff --git a/core/lib/alchemy/zippy/composer.lock b/core/lib/alchemy/zippy/composer.lock deleted file mode 100644 index e297e5d7e..000000000 --- a/core/lib/alchemy/zippy/composer.lock +++ /dev/null @@ -1,1092 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" - ], - "hash": "35caa45c80a3b84aae65b0ce71d21510", - "packages": [ - { - "name": "doctrine/collections", - "version": "v1.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "v1.1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/v1.1", - "reference": "v1.1", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "http://jmsyst.com", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ], - "time": "2013-03-07 12:15:54" - }, - { - "name": "guzzle/guzzle", - "version": "v3.7.4", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "b170b028c6bb5799640e46c8803015b0f9a45ed9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b170b028c6bb5799640e46c8803015b0f9a45ed9", - "reference": "b170b028c6bb5799640e46c8803015b0f9a45ed9", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.3", - "symfony/event-dispatcher": ">=2.1" - }, - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - "require-dev": { - "doctrine/cache": "*", - "monolog/monolog": "1.*", - "phpunit/phpunit": "3.7.*", - "psr/log": "1.0.*", - "symfony/class-loader": "*", - "zendframework/zend-cache": "2.0.*", - "zendframework/zend-log": "2.0.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - }, - "autoload": { - "psr-0": { - "Guzzle\\Tests": "tests/", - "Guzzle": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ], - "time": "2013-10-02 20:47:00" - }, - { - "name": "pimple/pimple", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/fabpot/Pimple.git", - "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/Pimple/zipball/ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", - "reference": "ae11e57e8c2bb414b2ff93396dbbfc0eb92feb94", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ], - "time": "2013-03-08 08:21:40" - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.3.6", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/7fc72a7a346a1887d3968cc1ce5642a15cd182e9", - "reference": "7fc72a7a346a1887d3968cc1ce5642a15cd182e9", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~2.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:45:20" - }, - { - "name": "symfony/filesystem", - "version": "v2.3.6", - "target-dir": "Symfony/Component/Filesystem", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "2b8995042086c5552c94d33b5553c492e9cfc00e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/2b8995042086c5552c94d33b5553c492e9cfc00e", - "reference": "2b8995042086c5552c94d33b5553c492e9cfc00e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:45:20" - }, - { - "name": "symfony/process", - "version": "v2.3.6", - "target-dir": "Symfony/Component/Process", - "source": { - "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "81191e354fe9dad0451036ccf0fdf283649d3f1e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/81191e354fe9dad0451036ccf0fdf283649d3f1e", - "reference": "81191e354fe9dad0451036ccf0fdf283649d3f1e", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Process\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "http://symfony.com", - "time": "2013-10-09 21:17:57" - } - ], - "packages-dev": [ - { - "name": "michelf/php-markdown", - "version": "1.3", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-markdown.git", - "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-markdown/zipball/fcdd3e0781ae40c2b9847874e0755ff4f5559688", - "reference": "fcdd3e0781ae40c2b9847874e0755ff4f5559688", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-lib": "1.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "http://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "http://daringfireball.net/" - } - ], - "description": "PHP Markdown", - "homepage": "http://michelf.ca/projects/php-markdown/", - "keywords": [ - "markdown" - ], - "time": "2013-04-11 18:53:11" - }, - { - "name": "nikic/php-parser", - "version": "v0.9.4", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1e5e280ae88a27effa2ae4aa2bd088494ed8594f", - "reference": "1e5e280ae88a27effa2ae4aa2bd088494ed8594f", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.9-dev" - } - }, - "autoload": { - "psr-0": { - "PHPParser": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "time": "2013-08-25 17:11:40" - }, - { - "name": "phpunit/php-code-coverage", - "version": "1.2.13", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.1.1@stable", - "phpunit/php-token-stream": ">=1.1.3@stable" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*@dev" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.0.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2013-09-10 08:14:32" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.3.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "File/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2013-10-10 15:34:57" - }, - { - "name": "phpunit/php-text-template", - "version": "1.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5180896f51c5b3648ac946b05f9ec02be78a0b23", - "reference": "5180896f51c5b3648ac946b05f9ec02be78a0b23", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "Text/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2012-10-31 18:15:28" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2013-08-02 07:42:54" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2013-09-13 04:58:23" - }, - { - "name": "phpunit/phpunit", - "version": "3.7.28", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", - "reference": "3b97c8492bcafbabe6b6fbd2ab35f2f04d932a8d", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2.1", - "phpunit/php-file-iterator": ">=1.3.1", - "phpunit/php-text-template": ">=1.1.1", - "phpunit/php-timer": ">=1.0.4", - "phpunit/phpunit-mock-objects": "~1.2.0", - "symfony/yaml": "~2.0" - }, - "require-dev": { - "pear-pear/pear": "1.9.4" - }, - "suggest": { - "ext-json": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "phpunit/php-invoker": ">=1.1.0,<1.2.0" - }, - "bin": [ - "composer/bin/phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2013-10-17 07:27:40" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "1.2.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2013-01-13 10:24:48" - }, - { - "name": "sami/sami", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/fabpot/Sami.git", - "reference": "c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/Sami/zipball/c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe", - "reference": "c0bc11b187a2f57414e2bf8dd7b40455e9b21ffe", - "shasum": "" - }, - "require": { - "michelf/php-markdown": "~1.3", - "nikic/php-parser": "0.9.*", - "php": ">=5.3.0", - "pimple/pimple": "1.0.*", - "symfony/console": "~2.1", - "symfony/filesystem": "~2.1", - "symfony/finder": "~2.1", - "symfony/process": "~2.1", - "symfony/yaml": "~2.1", - "twig/twig": "1.*" - }, - "bin": [ - "sami.php" - ], - "type": "application", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, - "autoload": { - "psr-0": { - "Sami": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Sami, an API documentation generator", - "homepage": "http://sami.sensiolabs.org", - "keywords": [ - "phpdoc" - ], - "time": "2013-10-18 19:34:10" - }, - { - "name": "symfony/console", - "version": "v2.3.6", - "target-dir": "Symfony/Component/Console", - "source": { - "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "f880062d56edefb25b36f2defa65aafe65959dc7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/f880062d56edefb25b36f2defa65aafe65959dc7", - "reference": "f880062d56edefb25b36f2defa65aafe65959dc7", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/event-dispatcher": "~2.1" - }, - "suggest": { - "symfony/event-dispatcher": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Console\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "http://symfony.com", - "time": "2013-09-25 06:04:15" - }, - { - "name": "symfony/finder", - "version": "v2.3.6", - "target-dir": "Symfony/Component/Finder", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/a175521f680b178e63c5d0ab87c6b046c0990c3f", - "reference": "a175521f680b178e63c5d0ab87c6b046c0990c3f", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:45:20" - }, - { - "name": "symfony/yaml", - "version": "v2.3.6", - "target-dir": "Symfony/Component/Yaml", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/6bb881b948368482e1abf1a75c08bcf88a1c5fc3", - "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2013-09-22 18:04:39" - }, - { - "name": "twig/twig", - "version": "v1.14.2", - "source": { - "type": "git", - "url": "https://github.com/fabpot/Twig.git", - "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/Twig/zipball/ca445842fcea4f844d68203ffa2d00f5e3cdea64", - "reference": "ca445842fcea4f844d68203ffa2d00f5e3cdea64", - "shasum": "" - }, - "require": { - "php": ">=5.2.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.14-dev" - } - }, - "autoload": { - "psr-0": { - "Twig_": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Armin Ronacher", - "email": "armin.ronacher@active-4.com" - } - ], - "description": "Twig, the flexible, fast, and secure template language for PHP", - "homepage": "http://twig.sensiolabs.org", - "keywords": [ - "templating" - ], - "time": "2013-10-30 08:20:53" - } - ], - "aliases": [ - - ], - "minimum-stability": "stable", - "stability-flags": { - "sami/sami": 20 - }, - "platform": { - "php": ">=5.3.3" - }, - "platform-dev": { - "ext-zip": "*" - } -} diff --git a/core/lib/alchemy/zippy/phpunit-functional.xml.dist b/core/lib/alchemy/zippy/phpunit-functional.xml.dist deleted file mode 100644 index 5429609ea..000000000 --- a/core/lib/alchemy/zippy/phpunit-functional.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - tests/Alchemy/Zippy/Functional - - - - - vendor - tests - - - - - diff --git a/core/lib/alchemy/zippy/phpunit.xml.dist b/core/lib/alchemy/zippy/phpunit.xml.dist deleted file mode 100644 index 005cf4fc9..000000000 --- a/core/lib/alchemy/zippy/phpunit.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - tests/Alchemy/Zippy/Tests - - - - - vendor - tests - - - - - diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php deleted file mode 100644 index 4060264fa..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractAdapter.php +++ /dev/null @@ -1,246 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Archive\Archive; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Resource\ResourceManager; -use Alchemy\Zippy\Adapter\VersionProbe\VersionProbeInterface; -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; - -abstract class AbstractAdapter implements AdapterInterface -{ - /** @var ResourceManager */ - protected $manager; - - /** - * The version probe - * - * @var VersionProbeInterface - */ - protected $probe; - - public function __construct(ResourceManager $manager) - { - $this->manager = $manager; - } - - /** - * @inheritdoc - */ - public function open($path) - { - $this->requireSupport(); - - return new Archive($this->createResource($path), $this, $this->manager); - } - - /** - * @inheritdoc - */ - public function create($path, $files = null, $recursive = true) - { - $this->requireSupport(); - - return $this->doCreate($this->makeTargetAbsolute($path), $files, $recursive); - } - - /** - * @inheritdoc - */ - public function listMembers(ResourceInterface $resource) - { - $this->requireSupport(); - - return $this->doListMembers($resource); - } - - /** - * @inheritdoc - */ - public function add(ResourceInterface $resource, $files, $recursive = true) - { - $this->requireSupport(); - - return $this->doAdd($resource, $files, $recursive); - } - - /** - * @inheritdoc - */ - public function remove(ResourceInterface $resource, $files) - { - $this->requireSupport(); - - return $this->doRemove($resource, $files); - } - - /** - * @inheritdoc - */ - public function extract(ResourceInterface $resource, $to = null) - { - $this->requireSupport(); - - return $this->doExtract($resource, $to); - } - - /** - * @inheritdoc - */ - public function extractMembers(ResourceInterface $resource, $members, $to = null) - { - $this->requireSupport(); - - return $this->doExtractMembers($resource, $members, $to); - } - - /** - * Returns the version probe used by this adapter - * - * @return VersionProbeInterface - */ - public function getVersionProbe() - { - return $this->probe; - } - - /** - * Sets the version probe used by this adapter - * - * @return VersionProbeInterface - */ - public function setVersionProbe(VersionProbeInterface $probe) - { - $this->probe = $probe; - - return $this; - } - - /** - * @inheritdoc - */ - public function isSupported() - { - if (!$this->probe) { - throw new RuntimeException(sprintf( - 'No version probe has been set on %s whereas it is required', get_class($this) - )); - } - - return VersionProbeInterface::PROBE_OK === $this->probe->getStatus(); - } - - /** - * Throws an exception is the current adapter is not supported - * - * @throws RuntimeException - */ - protected function requireSupport() - { - if (false === $this->isSupported()) { - throw new RuntimeException(sprintf('%s is not supported on your system', get_class($this))); - } - } - - /** - * Change current working directory to another - * - * @param string $target the target directory - * - * @return AdapterInterface - * - * @throws RuntimeException In case of failure - */ - protected function chdir($target) - { - if (false === @chdir($target)) { - throw new RuntimeException(sprintf('Unable to chdir to `%s`', $target)); - } - - return $this; - } - - /** - * Creates a resource given a path - * - * @return ResourceInterface - */ - abstract protected function createResource($path); - - /** - * Do the removal after having check that the current adapter is supported - * - * @return Array - */ - abstract protected function doRemove(ResourceInterface $resource, $files); - - /** - * Do the add after having check that the current adapter is supported - * - * @return Array - */ - abstract protected function doAdd(ResourceInterface $resource, $files, $recursive); - - /** - * Do the extract after having check that the current adapter is supported - * - * @return \SplFileInfo The extracted archive - */ - abstract protected function doExtract(ResourceInterface $resource, $to); - - /** - * Do the extract members after having check that the current adapter is supported - * - * @return \SplFileInfo The extracted archive - */ - abstract protected function doExtractMembers(ResourceInterface $resource, $members, $to); - - /** - * Do the list members after having check that the current adapter is supported - * - * @return Array - */ - abstract protected function doListMembers(ResourceInterface $resource); - - /** - * Do the create after having check that the current adapter is supported - * - * @return ArchiveInterface - */ - abstract protected function doCreate($path, $file, $recursive); - - /** - * Makes the target path absolute as the adapters might have a different directory - * - * @param $path The path to convert - * - * @return string The absolute path - * - * @throws InvalidArgumentException In case the path is not writable or does not exist - */ - private function makeTargetAbsolute($path) - { - $directory = dirname($path); - - if (!is_dir($directory)) { - throw new InvalidArgumentException(sprintf('Target path %s does not exist.', $directory)); - } - if (!is_writable($directory)) { - throw new InvalidArgumentException(sprintf('Target path %s is not writeable.', $directory)); - } - - return realpath($directory).'/'.basename ($path); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php deleted file mode 100644 index 8aa1ff38f..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractBinaryAdapter.php +++ /dev/null @@ -1,231 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Adapter\Resource\FileResource; -use Alchemy\Zippy\Archive\MemberInterface; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\Parser\ParserInterface; -use Alchemy\Zippy\Parser\ParserFactory; -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactory; -use Alchemy\Zippy\Resource\ResourceManager; -use Symfony\Component\Process\ExecutableFinder; -use Symfony\Component\Process\ProcessBuilder; - -abstract class AbstractBinaryAdapter extends AbstractAdapter implements BinaryAdapterInterface -{ - /** - * The parser to use to parse command output - * - * @var ParserInterface - */ - protected $parser; - - /** - * The deflator process builder factory to use to build binary command line - * - * @var ProcessBuilderFactoryInterface - */ - protected $deflator; - - /** - * The inflator process builder factory to use to build binary command line - * - * @var ProcessBuilderFactoryInterface - */ - protected $inflator; - - /** - * Constructor - * - * @param ParserInterface $parser An output parser - * @param ResourceManager $manager A resource manager - * @param ProcessBuilderFactoryInterface $inflator A process builder factory for the inflator binary - * @param ProcessBuilderFactoryInterface|null $deflator A process builder factory for the deflator binary - */ - public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) - { - $this->parser = $parser; - $this->manager = $manager; - $this->deflator = $deflator; - $this->inflator = $inflator; - } - - /** - * @inheritdoc - */ - public function getParser() - { - return $this->parser; - } - - /** - * @inheritdoc - */ - public function setParser(ParserInterface $parser) - { - $this->parser = $parser; - - return $this; - } - - /** - * @inheritdoc - */ - public function getDeflator() - { - return $this->deflator; - } - - /** - * @inheritdoc - */ - public function getInflator() - { - return $this->inflator; - } - - /** - * @inheritdoc - */ - public function setDeflator(ProcessBuilderFactoryInterface $processBuilder) - { - $this->deflator = $processBuilder; - - return $this; - } - - public function setInflator(ProcessBuilderFactoryInterface $processBuilder) - { - $this->inflator = $processBuilder; - - return $this; - } - - /** - * @inheritdoc - */ - public function getInflatorVersion() - { - $this->requireSupport(); - - return $this->doGetInflatorVersion(); - } - - /** - * @inheritdoc - */ - public function getDeflatorVersion() - { - $this->requireSupport(); - - return $this->doGetDeflatorVersion(); - } - - /** - * Returns a new instance of the invoked adapter - * - * @params String|null $inflatorBinaryName The inflator binary name to use - * @params String|null $deflatorBinaryName The deflator binary name to use - * - * @return AbstractBinaryAdapter - * - * @throws RuntimeException In case object could not be instanciated - */ - public static function newInstance(ExecutableFinder $finder, ResourceManager $manager, $inflatorBinaryName = null, $deflatorBinaryName = null) - { - $inflator = $inflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $inflatorBinaryName : self::findABinary($inflatorBinaryName, static::getDefaultInflatorBinaryName(), $finder); - $deflator = $deflatorBinaryName instanceof ProcessBuilderFactoryInterface ? $deflatorBinaryName : self::findABinary($deflatorBinaryName, static::getDefaultDeflatorBinaryName(), $finder); - - try { - $outputParser = ParserFactory::create(static::getName()); - } catch (InvalidArgumentException $e) { - throw new RuntimeException(sprintf( - 'Failed to get a new instance of %s', - get_called_class()), $e->getCode(), $e - ); - } - - if (null === $inflator) { - throw new RuntimeException(sprintf('Unable to create the inflator')); - } - - if (null === $deflator) { - throw new RuntimeException(sprintf('Unable to create the deflator')); - } - - return new static($outputParser, $manager, $inflator, $deflator); - } - - private static function findABinary($wish, array $defaults, ExecutableFinder $finder) - { - $possibles = $wish ? (array) $wish : $defaults; - - $binary = null; - - foreach ($possibles as $possible) { - if (null !== $found = $finder->find($possible)) { - $binary = new ProcessBuilderFactory($found); - break; - } - } - - return $binary; - } - - /** - * Adds files to argument list - * - * @param Array $files An array of files - * @param ProcessBuilder $builder A Builder instance - * - * @return Boolean - */ - protected function addBuilderFileArgument(array $files, ProcessBuilder $builder) - { - $iterations = 0; - - array_walk($files, function ($file) use ($builder, &$iterations) { - $builder->add( - $file instanceof \SplFileInfo ? - $file->getRealpath() : - ($file instanceof MemberInterface ? $file->getLocation() : $file) - ); - - $iterations++; - }); - - return 0 !== $iterations; - } - - protected function createResource($path) - { - return new FileResource($path); - } - - /** - * Fetch the inflator version after having check that the current adapter is supported - * - * @return string - */ - abstract protected function doGetInflatorVersion(); - - /** - * Fetch the Deflator version after having check that the current adapter is supported - * - * @return string - */ - abstract protected function doGetDeflatorVersion(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php deleted file mode 100644 index f865b2c49..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AbstractTarAdapter.php +++ /dev/null @@ -1,424 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; -use Alchemy\Zippy\Archive\Archive; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Archive\Member; -use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException; - -abstract class AbstractTarAdapter extends AbstractBinaryAdapter -{ - /** - * @inheritdoc - */ - protected function doCreate($path, $files, $recursive) - { - return $this->doTarCreate($this->getLocalOptions(), $path, $files, $recursive); - } - - /** - * @inheritdoc - */ - protected function doListMembers(ResourceInterface $resource) - { - return $this->doTarListMembers($this->getLocalOptions(), $resource); - } - - /** - * @inheritdoc - */ - protected function doAdd(ResourceInterface $resource, $files, $recursive) - { - return $this->doTarAdd($this->getLocalOptions(), $resource, $files, $recursive); - } - - /** - * @inheritdoc - */ - protected function doRemove(ResourceInterface $resource, $files) - { - return $this->doTarRemove($this->getLocalOptions(), $resource, $files); - } - - /** - * @inheritdoc - */ - protected function doExtractMembers(ResourceInterface $resource, $members, $to) - { - return $this->doTarExtractMembers($this->getLocalOptions(), $resource, $members, $to); - } - - /** - * @inheritdoc - */ - protected function doExtract(ResourceInterface $resource, $to) - { - return $this->doTarExtract($this->getLocalOptions(), $resource, $to); - } - - /** - * @inheritdoc - */ - protected function doGetInflatorVersion() - { - $process = $this - ->inflator - ->create() - ->add('--version') - ->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), $process->getErrorOutput() - )); - } - - return $this->parser->parseInflatorVersion($process->getOutput() ? : ''); - } - - /** - * @inheritdoc - */ - protected function doGetDeflatorVersion() - { - return $this->getInflatorVersion(); - } - - protected function doTarCreate($options, $path, $files = null, $recursive = true) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - if (!$recursive) { - $builder->add('--no-recursion'); - } - - $builder->add('--create'); - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - if (0 === count($files)) { - $nullFile = defined('PHP_WINDOWS_VERSION_BUILD') ? 'NUL' : '/dev/null'; - - $builder->add('-'); - $builder->add(sprintf('--files-from %s', $nullFile)); - $builder->add(sprintf('> %s', $path)); - - $process = $builder->getProcess(); - $process->run(); - - } else { - - $builder->add(sprintf('--file=%s', $path)); - - if (!$recursive) { - $builder->add('--no-recursion'); - } - - $collection = $this->manager->handle(getcwd(), $files); - - $builder->setWorkingDirectory($collection->getContext()); - - $collection->forAll(function ($i, Resource $resource) use ($builder) { - return $builder->add($resource->getTarget()); - }); - - $process = $builder->getProcess(); - - try { - $process->run(); - } catch (ProcessException $e) { - $this->manager->cleanup($collection); - throw $e; - } - - $this->manager->cleanup($collection); - } - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return new Archive($this->createResource($path), $this, $this->manager); - } - - protected function doTarListMembers($options, ResourceInterface $resource) - { - $builder = $this - ->inflator - ->create(); - - foreach ($this->getListMembersOptions() as $option) { - $builder->add($option); - } - - $builder - ->add('--list') - ->add('-v') - ->add(sprintf('--file=%s', $resource->getResource())); - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - $process = $builder->getProcess(); - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - $members = array(); - - foreach ($this->parser->parseFileListing($process->getOutput() ? : '') as $member) { - $members[] = new Member( - $resource, - $this, - $member['location'], - $member['size'], - $member['mtime'], - $member['is_dir'] - ); - } - - return $members; - } - - protected function doTarAdd($options, ResourceInterface $resource, $files, $recursive = true) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - if (!$recursive) { - $builder->add('--no-recursion'); - } - - $builder - ->add('--append') - ->add(sprintf('--file=%s', $resource->getResource())); - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - // there will be an issue if the file starts with a dash - // see --add-file=FILE - $collection = $this->manager->handle(getcwd(), $files); - - $builder->setWorkingDirectory($collection->getContext()); - - $collection->forAll(function ($i, Resource $resource) use ($builder) { - return $builder->add($resource->getTarget()); - }); - - $process = $builder->getProcess(); - - try { - $process->run(); - } catch (ProcessException $e) { - $this->manager->cleanup($collection); - throw $e; - } - - $this->manager->cleanup($collection); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $files; - } - - protected function doTarRemove($options, ResourceInterface $resource, $files) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - $builder - ->add('--delete') - ->add(sprintf('--file=%s', $resource->getResource())); - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - if (!$this->addBuilderFileArgument($files, $builder)) { - throw new InvalidArgumentException('Invalid files'); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $files; - } - - protected function doTarExtract($options, ResourceInterface $resource, $to = null) - { - if (null !== $to && !is_dir($to)) { - throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); - } - - $builder = $this - ->inflator - ->create(); - - $builder - ->add('--extract') - ->add(sprintf('--file=%s', $resource->getResource())); - - foreach ($this->getExtractOptions() as $option) { - $builder - ->add($option); - } - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - if (null !== $to) { - $builder - ->add('--directory') - ->add($to); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return new \SplFileInfo($to ? : $resource->getResource()); - } - - protected function doTarExtractMembers($options, ResourceInterface $resource, $members, $to = null) - { - if (null !== $to && !is_dir($to)) { - throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); - } - - $members = (array) $members; - - $builder = $this - ->inflator - ->create(); - - $builder - ->add('--extract') - ->add(sprintf('--file=%s', $resource->getResource())); - - foreach ($this->getExtractMembersOptions() as $option) { - $builder - ->add($option); - } - - foreach ((array) $options as $option) { - $builder->add((string) $option); - } - - if (null !== $to) { - $builder - ->add('--directory') - ->add($to); - } - - if (!$this->addBuilderFileArgument($members, $builder)) { - throw new InvalidArgumentException('Invalid files'); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $members; - } - - /** - * Returns an array of option for the listMembers command - * - * @return Array - */ - abstract protected function getListMembersOptions(); - - /** - * Returns an array of option for the extract command - * - * @return Array - */ - abstract protected function getExtractOptions(); - - /** - * Returns an array of option for the extractMembers command - * - * @return Array - */ - abstract protected function getExtractMembersOptions(); - - /** - * Gets adapter specific additional options - * - * @return Array - */ - abstract protected function getLocalOptions(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php deleted file mode 100644 index ee3b18890..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterContainer.php +++ /dev/null @@ -1,148 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Adapter\BSDTar\TarBSDTarAdapter; -use Alchemy\Zippy\Adapter\BSDTar\TarGzBSDTarAdapter; -use Alchemy\Zippy\Adapter\BSDTar\TarBz2BSDTarAdapter; -use Alchemy\Zippy\Adapter\GNUTar\TarGNUTarAdapter; -use Alchemy\Zippy\Adapter\GNUTar\TarGzGNUTarAdapter; -use Alchemy\Zippy\Adapter\GNUTar\TarBz2GNUTarAdapter; -use Alchemy\Zippy\Resource\ResourceManager; -use Alchemy\Zippy\Resource\RequestMapper; -use Alchemy\Zippy\Resource\TeleporterContainer; -use Alchemy\Zippy\Resource\ResourceTeleporter; -use Alchemy\Zippy\Resource\TargetLocator; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Process\ExecutableFinder; - -class AdapterContainer extends \Pimple -{ - /** - * Builds the adapter container - * - * @return AdapterContainer - */ - public static function load() - { - $container = new static(); - - $container['zip.inflator'] = null; - $container['zip.deflator'] = null; - - $container['resource-manager'] = $container->share(function ($container) { - return new ResourceManager( - $container['request-mapper'], - $container['resource-teleporter'], - $container['filesystem'] - ); - }); - - $container['executable-finder'] = $container->share(function ($container) { - return new ExecutableFinder(); - }); - - $container['request-mapper'] = $container->share(function ($container) { - return new RequestMapper($container['target-locator']); - }); - - $container['target-locator'] = $container->share(function () { - return new TargetLocator(); - }); - - $container['teleporter-container'] = $container->share(function ($container) { - return TeleporterContainer::load(); - }); - - $container['resource-teleporter'] = $container->share(function ($container) { - return new ResourceTeleporter($container['teleporter-container']); - }); - - $container['filesystem'] = $container->share(function () { - return new Filesystem(); - }); - - $container['Alchemy\\Zippy\\Adapter\\ZipAdapter'] = $container->share(function ($container) { - return ZipAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['zip.inflator'], - $container['zip.deflator'] - ); - }); - - $container['gnu-tar.inflator'] = null; - $container['gnu-tar.deflator'] = null; - - $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter'] = $container->share(function ($container) { - return TarGNUTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['gnu-tar.inflator'], - $container['gnu-tar.deflator'] - ); - }); - - $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'] = $container->share(function ($container) { - return TarGzGNUTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['gnu-tar.inflator'], - $container['gnu-tar.deflator'] - ); - }); - - $container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'] = $container->share(function ($container) { - return TarBz2GNUTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['gnu-tar.inflator'], - $container['gnu-tar.deflator'] - ); - }); - - $container['bsd-tar.inflator'] = null; - $container['bsd-tar.deflator'] = null; - - $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'] = $container->share(function ($container) { - return TarBSDTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['bsd-tar.inflator'], - $container['bsd-tar.deflator'] - ); - }); - - $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'] = $container->share(function ($container) { - return TarGzBSDTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['bsd-tar.inflator'], - $container['bsd-tar.deflator'] - ); - }); - - $container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'] = $container->share(function ($container) { - return TarBz2BSDTarAdapter::newInstance( - $container['executable-finder'], - $container['resource-manager'], - $container['bsd-tar.inflator'], - $container['bsd-tar.deflator']); - }); - - $container['Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'] = $container->share(function () { - return ZipExtensionAdapter::newInstance(); - }); - - return $container; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php deleted file mode 100644 index aaa0a3d24..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/AdapterInterface.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Archive\ArchiveInterface; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Exception\RuntimeException; - -Interface AdapterInterface -{ - /** - * Opens an archive - * - * @param String $path The path to the archive - * - * @return ArchiveInterface - * - * @throws InvalidArgumentException In case the provided path is not valid - * @throws RuntimeException In case of failure - */ - public function open($path); - - /** - * Creates a new archive - * - * Please note some adapters can not create empty archives. - * They would throw a `NotSupportedException` in case you ask to create an archive without files - * - * @param String $path The path to the archive - * @param String|Array|\Traversable|null $files A filename, an array of files, or a \Traversable instance - * @param Boolean $recursive Whether to recurse or not in the provided directories - * - * @return ArchiveInterface - * - * @throws RuntimeException In case of failure - * @throws NotSupportedException In case the operation in not supported - * @throws InvalidArgumentException In case no files could be added - */ - public function create($path, $files = null, $recursive = true); - - /** - * Tests if the adapter is supported by the current environment - * - * @return Boolean - */ - public function isSupported(); - - /** - * Returns the list of all archive members - * - * @param ResourceInterface $resource The path to the archive - * - * @return Array - * - * @throws RuntimeException In case of failure - */ - public function listMembers(ResourceInterface $resource); - - /** - * Adds a file to the archive - * - * @param ResourceInterface $resource The path to the archive - * @param String|Array|\Traversable $files An array of paths to add, relative to cwd - * @param Boolean $recursive Whether or not to recurse in the provided directories - * - * @return Array - * - * @throws RuntimeException In case of failure - * @throws InvalidArgumentException In case no files could be added - */ - public function add(ResourceInterface $resource, $files, $recursive = true); - - /** - * Removes a member of the archive - * - * @param ResourceInterface $resource The path to the archive - * @param String|Array|\Traversable $files A filename, an array of files, or a \Traversable instance - * - * @return Array - * - * @throws RuntimeException In case of failure - * @throws InvalidArgumentException In case no files could be removed - */ - public function remove(ResourceInterface $resource, $files); - - /** - * Extracts an entire archive - * - * Note that any existing files will be overwritten by the adapter - * - * @param ResourceInterface $resource The path to the archive - * @param String|null $to The path where to extract the archive - * - * @return \SplFileInfo The extracted archive - * - * @throws RuntimeException In case of failure - * @throws InvalidArgumentException In case the provided path where to extract the archive is not valid - */ - public function extract(ResourceInterface $resource, $to = null); - - /** - * Extracts specific members of the archive - * - * @param ResourceInterface $resource The path to the archive - * @param Array $members An array of members - * @param String|null $to The path where to extract the members - * - * @return \SplFileInfo The extracted archive - * - * @throws RuntimeException In case of failure - * @throws InvalidArgumentException In case no members could be removed or provide extract target directory is not valid - */ - public function extractMembers(ResourceInterface $resource, $members, $to = null); - - /** - * Returns the adapter name - * - * @return String - */ - public static function getName(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php deleted file mode 100644 index 13a523fd1..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBSDTarAdapter.php +++ /dev/null @@ -1,79 +0,0 @@ -probe = new BSDTarVersionProbe($inflator, $deflator); - } - - /** - * @inheritdoc - */ - protected function getLocalOptions() - { - return array(); - } - - /** - * @inheritdoc - */ - public static function getName() - { - return 'bsd-tar'; - } - - /** - * @inheritdoc - */ - public static function getDefaultDeflatorBinaryName() - { - return array('bsdtar', 'tar'); - } - - /** - * @inheritdoc - */ - public static function getDefaultInflatorBinaryName() - { - return array('bsdtar', 'tar'); - } - - /** - * {@inheritdoc} - */ - protected function getListMembersOptions() - { - return array(); - } - - /** - * {@inheritdoc} - */ - protected function getExtractOptions() - { - return array(); - } - - /** - * {@inheritdoc} - */ - protected function getExtractMembersOptions() - { - return array(); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php deleted file mode 100644 index b2c8d8dea..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/BSDTar/TarBz2BSDTarAdapter.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Parser\ParserInterface; -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; - -interface BinaryAdapterInterface -{ - /** - * Gets the output parser - * - * @return ParserInterface - */ - public function getParser(); - - /** - * Sets the parser - * - * @param ParserInterface $parser The parser to use - * - * @return AbstractBinaryAdapter - */ - public function setParser(ParserInterface $parser); - - /** - * Returns the inflator process builder - * - * @return ProcessBuilderFactoryInterface - */ - public function getInflator(); - - /** - * Sets the inflator process builder - * - * @param ProcessBuilderFactoryInterface $processBuilder The parser to use - * - * @return AbstractBinaryAdapter - */ - public function setInflator(ProcessBuilderFactoryInterface $processBuilder); - - /** - * Returns the deflator process builder - * - * @return ProcessBuilderFactoryInterface - */ - public function getDeflator(); - - /** - * Sets the deflator process builder - * - * @param ProcessBuilderFactoryInterface $processBuilder The parser to use - * - * @return AbstractBinaryAdapter - */ - public function setDeflator(ProcessBuilderFactoryInterface $processBuilder); - - /** - * Returns the inflator binary version - * - * @return String - */ - public function getInflatorVersion(); - - /** - * Returns the deflator binary version - * - * @return String - */ - public function getDeflatorVersion(); - - /** - * Gets the inflator adapter binary name - * - * @return Array - */ - public static function getDefaultInflatorBinaryName(); - - /** - * Gets the deflator adapter binary name - * - * @return Array - */ - public static function getDefaultDeflatorBinaryName(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php deleted file mode 100644 index d44dc2575..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarBz2GNUTarAdapter.php +++ /dev/null @@ -1,25 +0,0 @@ -probe = new GNUTarVersionProbe($inflator, $deflator); - } - - /** - * @inheritdoc - */ - protected function getLocalOptions() - { - return array(); - } - - /** - * @inheritdoc - */ - public static function getName() - { - return 'gnu-tar'; - } - - /** - * @inheritdoc - */ - public static function getDefaultDeflatorBinaryName() - { - return array('gnutar', 'tar'); - } - - /** - * @inheritdoc - */ - public static function getDefaultInflatorBinaryName() - { - return array('gnutar', 'tar'); - } - - /** - * {@inheritdoc} - */ - protected function getListMembersOptions() - { - return array('--utc'); - } - - /** - * {@inheritdoc} - */ - protected function getExtractOptions() - { - return array('--overwrite-dir', '--overwrite', '--strip-components=1'); - } - - /** - * {@inheritdoc} - */ - protected function getExtractMembersOptions() - { - return array('--overwrite-dir', '--overwrite', '--strip-components=1'); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php deleted file mode 100644 index 6bbf5cb44..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/GNUTar/TarGzGNUTarAdapter.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\Resource; - -class FileResource implements ResourceInterface -{ - private $path; - - public function __construct($path) - { - $this->path = $path; - } - - /** - * {@inheritdoc} - */ - public function getResource() - { - return $this->path; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php deleted file mode 100644 index a42d08a5c..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ResourceInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\Resource; - -interface ResourceInterface -{ - /** - * Returns the actual resource used by an adapter - * - * @return mixed - */ - public function getResource(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php deleted file mode 100644 index 4028324c4..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/Resource/ZipArchiveResource.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\Resource; - -class ZipArchiveResource implements ResourceInterface -{ - private $archive; - - public function __construct(\ZipArchive $archive) - { - $this->archive = $archive; - } - - /** - * {@inheritdoc} - */ - public function getResource() - { - return $this->archive; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php deleted file mode 100644 index 602d42006..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/AbstractTarVersionProbe.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; - -abstract class AbstractTarVersionProbe implements VersionProbeInterface -{ - private $isSupported; - private $inflator; - private $deflator; - - public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) - { - $this->inflator = $inflator; - $this->deflator = $deflator; - } - - /** - * {@inheritdoc} - */ - public function getStatus() - { - if (null !== $this->isSupported) { - return $this->isSupported; - } - - if (null === $this->inflator || null === $this->deflator) { - return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; - } - - $good = true; - - foreach (array($this->inflator, $this->deflator) as $builder) { - $process = $builder - ->create() - ->add('--version') - ->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; - } - - $lines = explode("\n", $process->getOutput(), 2); - $good = false !== stripos($lines[0], $this->getVersionSignature()); - - if (!$good) { - break; - } - } - - $this->isSupported = $good ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; - - return $this->isSupported; - } - - /** - * Returns the signature of inflator/deflator - * - * @return string - */ - abstract protected function getVersionSignature(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php deleted file mode 100644 index 6f374750e..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/BSDTarVersionProbe.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -class BSDTarVersionProbe extends AbstractTarVersionProbe -{ - /** - * {@inheritdoc} - */ - protected function getVersionSignature() - { - return 'bsdtar'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php deleted file mode 100644 index 046a836ad..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/GNUTarVersionProbe.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -class GNUTarVersionProbe extends AbstractTarVersionProbe -{ - /** - * {@inheritdoc} - */ - protected function getVersionSignature() - { - return '(gnu tar)'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php deleted file mode 100644 index a0c03721b..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/VersionProbeInterface.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -interface VersionProbeInterface -{ - const PROBE_OK = 0; - const PROBE_NOTSUPPORTED = 1; - - /** - * Probes for the support of an adapter. - * - * @return integer One of the self::PROBE_* constants - */ - public function getStatus(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php deleted file mode 100644 index d95d0b53d..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipExtensionVersionProbe.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -class ZipExtensionVersionProbe implements VersionProbeInterface -{ - /** - * {@inheritdoc} - */ - public function getStatus() - { - return class_exists('\ZipArchive') ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php deleted file mode 100644 index 12595cbf9..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/VersionProbe/ZipVersionProbe.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - * - */ - -namespace Alchemy\Zippy\Adapter\VersionProbe; - -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; - -class ZipVersionProbe implements VersionProbeInterface -{ - private $isSupported; - private $inflator; - private $deflator; - - public function __construct(ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) - { - $this->inflator = $inflator; - $this->deflator = $deflator; - } - - /** - * Set the inflator to zip - * - * @param ProcessBuilderFactoryInterface $inflator - * @return ZipVersionProbe - */ - public function setInflator(ProcessBuilderFactoryInterface $inflator) - { - $this->inflator = $inflator; - - return $this; - } - - /** - * Set the deflator to unzip - * - * @param ProcessBuilderFactoryInterface $deflator - * @return ZipVersionProbe - */ - public function setDeflator(ProcessBuilderFactoryInterface $deflator) - { - $this->deflator = $deflator; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getStatus() - { - if (null !== $this->isSupported) { - return $this->isSupported; - } - - if (null === $this->inflator || null === $this->deflator) { - return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; - } - - $processDeflate = $this - ->deflator - ->create() - ->add('-h') - ->getProcess(); - - $processDeflate->run(); - - $processInflate = $this - ->inflator - ->create() - ->add('-h') - ->getProcess(); - - $processInflate->run(); - - if (false === $processDeflate->isSuccessful() || false === $processInflate->isSuccessful()) { - return $this->isSupported = VersionProbeInterface::PROBE_NOTSUPPORTED; - } - - $lines = explode("\n", $processInflate->getOutput(), 2); - $inflatorOk = false !== stripos($lines[0], 'Info-ZIP'); - - $lines = explode("\n", $processDeflate->getOutput(), 2); - $deflatorOk = false !== stripos($lines[0], 'Info-ZIP'); - - return $this->isSupported = ($inflatorOk && $deflatorOk) ? VersionProbeInterface::PROBE_OK : VersionProbeInterface::PROBE_NOTSUPPORTED; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php deleted file mode 100644 index 3d3a0353a..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipAdapter.php +++ /dev/null @@ -1,361 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Archive\Archive; -use Alchemy\Zippy\Archive\Member; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\Exception\NotSupportedException; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Resource\ResourceManager; -use Alchemy\Zippy\Adapter\VersionProbe\ZipVersionProbe; -use Alchemy\Zippy\Parser\ParserInterface; -use Alchemy\Zippy\ProcessBuilder\ProcessBuilderFactoryInterface; -use Symfony\Component\Process\Exception\ExceptionInterface as ProcessException; - -/** - * ZipAdapter allows you to create and extract files from archives using Zip - * - * @see http://www.gnu.org/software/tar/manual/tar.html - */ -class ZipAdapter extends AbstractBinaryAdapter -{ - public function __construct(ParserInterface $parser, ResourceManager $manager, ProcessBuilderFactoryInterface $inflator, ProcessBuilderFactoryInterface $deflator) - { - parent::__construct($parser, $manager, $inflator, $deflator); - $this->probe = new ZipVersionProbe($inflator, $deflator); - } - - /** - * @inheritdoc - */ - protected function doCreate($path, $files, $recursive) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - if (0 === count($files)) { - throw new NotSupportedException('Can not create empty zip archive'); - } - - if ($recursive) { - $builder->add('-r'); - } - - $builder->add($path); - - $collection = $this->manager->handle(getcwd(), $files); - $builder->setWorkingDirectory($collection->getContext()); - - $collection->forAll(function ($i, Resource $resource) use ($builder) { - return $builder->add($resource->getTarget()); - }); - - $process = $builder->getProcess(); - - try { - $process->run(); - } catch (ProcessException $e) { - $this->manager->cleanup($collection); - throw $e; - } - - $this->manager->cleanup($collection); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return new Archive($this->createResource($path), $this, $this->manager); - } - - /** - * @inheritdoc - */ - protected function doListMembers(ResourceInterface $resource) - { - $process = $this - ->deflator - ->create() - ->add('-l') - ->add($resource->getResource()) - ->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - $members = array(); - - foreach ($this->parser->parseFileListing($process->getOutput() ?: '') as $member) { - $members[] = new Member( - $resource, - $this, - $member['location'], - $member['size'], - $member['mtime'], - $member['is_dir'] - ); - } - - return $members; - } - - /** - * @inheritdoc - */ - protected function doAdd(ResourceInterface $resource, $files, $recursive) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - if ($recursive) { - $builder->add('-r'); - } - - $builder - ->add('-u') - ->add($resource->getResource()); - - $collection = $this->manager->handle(getcwd(), $files); - - $builder->setWorkingDirectory($collection->getContext()); - - $collection->forAll(function ($i, Resource $resource) use ($builder) { - return $builder->add($resource->getTarget()); - }); - - $process = $builder->getProcess(); - - try { - $process->run(); - } catch (ProcessException $e) { - $this->manager->cleanup($collection); - throw $e; - } - - $this->manager->cleanup($collection); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - } - - /** - * @inheritdoc - */ - protected function doGetDeflatorVersion() - { - $process = $this - ->deflator - ->create() - ->add('-h') - ->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $this->parser->parseDeflatorVersion($process->getOutput() ?: ''); - } - - /** - * @inheritdoc - */ - protected function doGetInflatorVersion() - { - $process = $this - ->inflator - ->create() - ->add('-h') - ->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $this->parser->parseInflatorVersion($process->getOutput() ?: ''); - } - - /** - * @inheritdoc - */ - protected function doRemove(ResourceInterface $resource, $files) - { - $files = (array) $files; - - $builder = $this - ->inflator - ->create(); - - $builder - ->add('-d') - ->add($resource->getResource()); - - if (!$this->addBuilderFileArgument($files, $builder)) { - throw new InvalidArgumentException('Invalid files'); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $files; - } - - /** - * @inheritdoc - */ - public static function getName() - { - return 'zip'; - } - - /** - * @inheritdoc - */ - public static function getDefaultDeflatorBinaryName() - { - return array('unzip'); - } - - /** - * @inheritdoc - */ - public static function getDefaultInflatorBinaryName() - { - return array('zip'); - } - - /** - * @inheritdoc - */ - protected function doExtract(ResourceInterface $resource, $to) - { - if (null !== $to && !is_dir($to)) { - throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); - } - - $builder = $this - ->deflator - ->create(); - - $builder - ->add('-o') - ->add($resource->getResource()); - - if (null !== $to) { - $builder - ->add('-d') - ->add($to); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return new \SplFileInfo($to ?: $resource->getResource()); - } - - /** - * @inheritdoc - */ - protected function doExtractMembers(ResourceInterface $resource, $members, $to) - { - if (null !== $to && !is_dir($to)) { - throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); - } - - $members = (array) $members; - - $builder = $this - ->deflator - ->create(); - - $builder - ->add($resource->getResource()); - - if (null !== $to) { - $builder - ->add('-d') - ->add($to); - } - - if (!$this->addBuilderFileArgument($members, $builder)) { - throw new InvalidArgumentException('Invalid files'); - } - - $process = $builder->getProcess(); - - $process->run(); - - if (!$process->isSuccessful()) { - throw new RuntimeException(sprintf( - 'Unable to execute the following command %s {output: %s}', - $process->getCommandLine(), - $process->getErrorOutput() - )); - } - - return $members; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php deleted file mode 100644 index 2d89606aa..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Adapter/ZipExtensionAdapter.php +++ /dev/null @@ -1,339 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Adapter; - -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Exception\NotSupportedException; -use Alchemy\Zippy\Archive\Member; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; -use Alchemy\Zippy\Adapter\Resource\ZipArchiveResource; -use Alchemy\Zippy\Adapter\VersionProbe\ZipExtensionVersionProbe; -use Alchemy\Zippy\Archive\Archive; -use Alchemy\Zippy\Resource\ResourceManager; -use Alchemy\Zippy\Resource\Resource; - -/** - * ZipExtensionAdapter allows you to create and extract files from archives - * using PHP Zip extension - * - * @see http://www.php.net/manual/en/book.zip.php - */ -class ZipExtensionAdapter extends AbstractAdapter -{ - private $errorCodesMapping = array( - \ZipArchive::ER_EXISTS => "File already exists", - \ZipArchive::ER_INCONS => "Zip archive inconsistent", - \ZipArchive::ER_INVAL => "Invalid argument", - \ZipArchive::ER_MEMORY => "Malloc failure", - \ZipArchive::ER_NOENT => "No such file", - \ZipArchive::ER_NOZIP => "Not a zip archive", - \ZipArchive::ER_OPEN => "Can't open file", - \ZipArchive::ER_READ => "Read error", - \ZipArchive::ER_SEEK => "Seek error" - ); - - public function __construct(ResourceManager $manager) - { - parent::__construct($manager); - $this->probe = new ZipExtensionVersionProbe(); - } - - /** - * @inheritdoc - */ - protected function doListMembers(ResourceInterface $resource) - { - $members = array(); - for ($i = 0; $i < $resource->getResource()->numFiles; $i++) { - $stat = $resource->getResource()->statIndex($i); - $members[] = new Member( - $resource, - $this, - $stat['name'], - $stat['size'], - new \DateTime('@' . $stat['mtime']), - 0 === strlen($resource->getResource()->getFromIndex($i, 1)) - ); - } - - return $members; - } - - /** - * @inheritdoc - */ - public static function getName() - { - return 'zip-extension'; - } - - /** - * @inheritdoc - */ - protected function doExtract(ResourceInterface $resource, $to) - { - return $this->extractMembers($resource, null, $to); - } - - /** - * @inheritdoc - */ - protected function doExtractMembers(ResourceInterface $resource, $members, $to) - { - if (null === $to) { - // if no destination is given, will extract to zip current folder - $to = dirname(realpath($resource->getResource()->filename)); - } - if (!is_dir($to)) { - $resource->getResource()->close(); - throw new InvalidArgumentException(sprintf("%s is not a directory", $to)); - } - if (!is_writable($to)) { - $resource->getResource()->close(); - throw new InvalidArgumentException(sprintf("%s is not writable", $to)); - } - if (null !== $members) { - $membersTemp = (array) $members; - if (empty($membersTemp)) { - $resource->getResource()->close(); - - throw new InvalidArgumentException("no members provided"); - } - $members = array(); - // allows $members to be an array of strings or array of Members - foreach ($membersTemp as $member) { - if ($member instanceof Member) { - $member = $member->getLocation(); - } - if ($resource->getResource()->locateName($member) === false) { - $resource->getResource()->close(); - - throw new InvalidArgumentException(sprintf('%s is not in the zip file', $member)); - } - $members[] = $member; - } - } - - if (!$resource->getResource()->extractTo($to, $members)) { - $resource->getResource()->close(); - - throw new InvalidArgumentException(sprintf('Unable to extract archive : %s', $resource->getResource()->getStatusString())); - } - - return new \SplFileInfo($to); - } - - /** - * @inheritdoc - */ - protected function doRemove(ResourceInterface $resource, $files) - { - $files = (array) $files; - - if (empty($files)) { - throw new InvalidArgumentException("no files provided"); - } - - // either remove all files or none in case of error - foreach ($files as $file) { - if ($resource->getResource()->locateName($file) === false) { - $resource->getResource()->unchangeAll(); - $resource->getResource()->close(); - - throw new InvalidArgumentException(sprintf('%s is not in the zip file', $file)); - } - if (!$resource->getResource()->deleteName($file)) { - $resource->getResource()->unchangeAll(); - $resource->getResource()->close(); - - throw new RuntimeException(sprintf('unable to remove %s', $file)); - } - } - $this->flush($resource->getResource()); - - return $files; - } - - /** - * @inheritdoc - */ - protected function doAdd(ResourceInterface $resource, $files, $recursive) - { - $files = (array) $files; - if (empty($files)) { - $resource->getResource()->close(); - throw new InvalidArgumentException("no files provided"); - } - $this->addEntries($resource, $files, $recursive); - - return $files; - } - - /** - * @inheritdoc - */ - protected function doCreate($path, $files, $recursive) - { - $files = (array) $files; - - if (empty($files)) { - throw new NotSupportedException("Cannot create an empty zip"); - } - - $resource = $this->getResource($path, \ZipArchive::CREATE); - $this->addEntries($resource, $files, $recursive); - - return new Archive($resource, $this, $this->manager); - } - - /** - * Returns a new instance of the invoked adapter - * - * @return AbstractAdapter - * - * @throws RuntimeException In case object could not be instanciated - */ - public static function newInstance() - { - return new ZipExtensionAdapter(ResourceManager::create()); - } - - protected function createResource($path) - { - return $this->getResource($path, \ZipArchive::CHECKCONS); - } - - private function getResource($path, $mode) - { - $zip = new \ZipArchive(); - $res = $zip->open($path, $mode); - - if ($res !== true) { - throw new RuntimeException($this->errorCodesMapping[$res]); - } - - return new ZipArchiveResource($zip); - } - - private function addEntries(ZipArchiveResource $zipresource, array $files, $recursive) - { - $stack = new \SplStack(); - - $error = null; - $cwd = getcwd(); - $collection = $this->manager->handle($cwd, $files); - - $this->chdir($collection->getContext()); - - $adapter = $this; - - try { - $collection->forAll(function ($i, Resource $resource) use ($zipresource, $stack, $recursive, $adapter) { - $adapter->checkReadability($zipresource->getResource(), $resource->getTarget()); - if (is_dir($resource->getTarget())) { - if ($recursive) { - $stack->push($resource->getTarget() . ((substr($resource->getTarget(), -1) === DIRECTORY_SEPARATOR) ? '' : DIRECTORY_SEPARATOR )); - } else { - $adapter->addEmptyDir($zipresource->getResource(), $resource->getTarget()); - } - } else { - $adapter->addFileToZip($zipresource->getResource(), $resource->getTarget()); - } - - return true; - }); - - // recursively add dirs - while (!$stack->isEmpty()) { - $dir = $stack->pop(); - // removes . and .. - $files = array_diff(scandir($dir), array(".", "..")); - if (count($files) > 0) { - foreach ($files as $file) { - $file = $dir . $file; - $this->checkReadability($zipresource->getResource(), $file); - if (is_dir($file)) { - $stack->push($file . DIRECTORY_SEPARATOR); - } else { - $this->addFileToZip($zipresource->getResource(), $file); - } - } - } else { - $this->addEmptyDir($zipresource->getResource(), $dir); - } - } - $this->flush($zipresource->getResource()); - - $this->manager->cleanup($collection); - } catch (\Exception $e) { - $error = $e; - } - - $this->chdir($cwd); - - if ($error) { - throw $error; - } - } - - /** - * @info is public for PHP 5.3 compatibility, should be private - */ - public function checkReadability(\ZipArchive $zip, $file) - { - if (!is_readable($file)) { - $zip->unchangeAll(); - $zip->close(); - - throw new InvalidArgumentException(sprintf('could not read %s', $file)); - } - } - - /** - * @info is public for PHP 5.3 compatibility, should be private - */ - public function addFileToZip(\ZipArchive $zip, $file) - { - if (!$zip->addFile($file)) { - $zip->unchangeAll(); - $zip->close(); - - throw new RuntimeException(sprintf('unable to add %s to the zip file', $file)); - } - } - - /** - * @info is public for PHP 5.3 compatibility, should be private - */ - public function addEmptyDir(\ZipArchive $zip, $dir) - { - if (!$zip->addEmptyDir($dir)) { - $zip->unchangeAll(); - $zip->close(); - - throw new RuntimeException(sprintf('unable to add %s to the zip file', $dir)); - } - } - - /** - * Flushes changes to the archive - * - * @param \ZipArchive $zip - */ - private function flush(\ZipArchive $zip) // flush changes by reopening the file - { - $path = $zip->filename; - $zip->close(); - $zip->open($path, \ZipArchive::CHECKCONS); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php deleted file mode 100644 index a1142bd09..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Archive.php +++ /dev/null @@ -1,136 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Archive; - -use Alchemy\Zippy\Adapter\AdapterInterface; -use Alchemy\Zippy\Resource\ResourceManager; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; - -/** - * Represents an archive - */ -class Archive implements ArchiveInterface -{ - /** - * The path to the archive - * - * @var String - */ - protected $path; - - /** - * The archive adapter - * - * @var AdapterInterface - */ - protected $adapter; - - /** - * An array of archive members - * - * @var Array - */ - protected $members = array(); - - /** - * @var ResourceInterface - */ - protected $resource; - - /** - * - * @var ResourceManager - */ - protected $manager; - - /** - * Constructor - * - * @param ResourceInterface $resource Path to the archive - * @param AdapterInterface $adapter An archive adapter - * @param ResourceManager $manager The resource manager - */ - public function __construct(ResourceInterface $resource, AdapterInterface $adapter, ResourceManager $manager) - { - $this->resource = $resource; - $this->adapter = $adapter; - $this->manager = $manager; - } - - /** - * @inheritdoc - */ - public function count() - { - return count($this->getMembers()); - } - - /** - * Returns an Iterator for the current archive - * - * This method implements the IteratorAggregate interface. - * - * @return \ArrayIterator An iterator - */ - public function getIterator() - { - return new \ArrayIterator($this->getMembers()); - } - - /** - * @inheritdoc - */ - public function getMembers() - { - return $this->members = $this->adapter->listMembers($this->resource); - } - - /** - * @inheritdoc - */ - public function addMembers($sources, $recursive = true) - { - $this->adapter->add($this->resource, $sources, $recursive); - - return $this; - } - - /** - * @inheritdoc - */ - public function removeMembers($sources) - { - $this->adapter->remove($this->resource, $sources); - - return $this; - } - - /** - * @inheritdoc - */ - public function extract($toDirectory) - { - $this->adapter->extract($this->resource, $toDirectory); - - return $this; - } - - /** - * @inheritdoc - */ - public function extractMembers($members, $toDirectory = null) - { - $this->adapter->extractMembers($this->resource, $members, $toDirectory); - - return $this; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php deleted file mode 100644 index fac5720a9..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/ArchiveInterface.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Archive; - -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Exception\RuntimeException; - -interface ArchiveInterface extends \IteratorAggregate, \Countable -{ - /** - * Adds a file or a folder into the archive - * - * @param String|Array|\SplFileInfo $sources The path to the file or a folder - * @param Boolean $recursive Recurse into sub-directories - * - * @return ArchiveInterface - * - * @throws InvalidArgumentException In case the provided source path is not valid - * @throws RuntimeException In case of failure - */ - public function addMembers($sources, $recursive = true); - - /** - * Removes a file from the archive - * - * @param String|Array $sources The path to an archive or a folder member - * - * @return ArchiveInterface - * - * @throws RuntimeException In case of failure - */ - public function removeMembers($sources); - - /** - * Lists files and directories archive members - * - * @return Array An array of File - * - * @throws RuntimeException In case archive could not be read - */ - public function getMembers(); - - /** - * Extracts current archive to the given directory - * - * @param String $toDirectory The path the extracted archive - * - * @return ArchiveInterface - * - * @throws RuntimeException In case archive could not be extracted - */ - public function extract($toDirectory); - - /** - * Extracts specific members from the archive - * - * @param String|Array $members An array of members path - * @param string $toDirectory The destination $directory - * - * @return ArchiveInterface - * - * @throws RuntimeException In case member could not be extracted - */ - public function extractMembers($members, $toDirectory = null); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php deleted file mode 100644 index c95190548..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/Member.php +++ /dev/null @@ -1,133 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Archive; - -use Alchemy\Zippy\Adapter\AdapterInterface; -use Alchemy\Zippy\Adapter\Resource\ResourceInterface; - -/** - * Represents a member of an archive. - */ -class Member implements MemberInterface -{ - /** - * The location of the file - * - * @var String - */ - private $location; - - /** - * Tells whether the archive member is a directory or not - * - * @var Boolean - */ - private $isDir; - - /** - * The uncompressed size of the file - * - * @var Integer - */ - private $size; - - /** - * The last modified date of the file - * - * @var \DateTime - */ - private $lastModifiedDate; - - /** - * The resource to the actual archive - * - * @var String - */ - private $resource; - - /** - * An adapter - * - * @var AdapterInterface - */ - private $adapter; - - /** - * Constructor - * - * @param ResourceInterface $resource The path of the archive which contain the member - * @param AdapterInterface $adapter The archive adapter interface - * @param String $location The path of the archive member - * @param Integer $fileSize The uncompressed file size - * @param \DateTime $lastModifiedDate The last modified date of the member - * @param Boolean $isDir Tells whether the member is a directory or not - */ - public function __construct(ResourceInterface $resource, AdapterInterface $adapter, $location, $fileSize, \DateTime $lastModifiedDate, $isDir) - { - $this->resource = $resource; - $this->adapter = $adapter; - $this->location = $location; - $this->isDir = $isDir; - $this->size = $fileSize; - $this->lastModifiedDate = $lastModifiedDate; - } - - /** - * @inheritdoc - */ - public function getLocation() - { - return $this->location; - } - - /** - * @inheritdoc - */ - public function isDir() - { - return $this->isDir; - } - - /** - * @inheritdoc - */ - public function getLastModifiedDate() - { - return $this->lastModifiedDate; - } - - /** - * @inheritdoc - */ - public function getSize() - { - return $this->size; - } - - /** - * @inheritdoc - */ - public function __toString() - { - return $this->location; - } - - /** - * {@inheritdoc} - */ - public function extract($to = null) - { - $this->adapter->extractMembers($this->resource, $this->location, $to); - - return new \SplFileInfo(sprintf('%s%s', rtrim(null === $to ? getcwd() : $to, '/'), $this->location)); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php deleted file mode 100644 index 786b155c5..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Archive/MemberInterface.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Archive; - -interface MemberInterface -{ - /** - * Gets the location of an archive member - * - * @return String - */ - public function getLocation(); - - /** - * Tells whether the member is a directory or not - * - * @return Boolean - */ - public function isDir(); - - /* - * Returns the last modified date of the member - * - * @return \DateTime - */ - public function getLastModifiedDate(); - - /** - * Returns the (uncompressed) size of the member - * - * If the size is unknown, returns -1 - * - * @return Integer - */ - public function getSize(); - - /** - * Extract the member from its archive - * - * Be careful using this method within a loop - * This will execute one extraction process for each file - * Prefer the use of ArchiveInterface::extractMembers in that use case - * - * @param String|null $to The path where to extract the member, if no path is not provided the member is extracted in the same directory of its archive - * - * @return \SplFileInfo The extracted file - * - * @throws RuntimeException In case of failure - * @throws InvalidArgumentException In case no members could be removed or provide extract target directory is not valid - */ - public function extract($to = null); - - /** - * @inheritdoc - */ - public function __toString(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php deleted file mode 100644 index 832d2eb6c..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/ExceptionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -interface ExceptionInterface -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php deleted file mode 100644 index 681ac739f..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/FormatNotSupportedException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class FormatNotSupportedException extends RuntimeException -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php deleted file mode 100644 index 6da5dd211..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/IOException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class IOException extends RuntimeException -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php deleted file mode 100644 index c901135fa..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php deleted file mode 100644 index 40f863651..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NoAdapterOnPlatformException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class NoAdapterOnPlatformException extends RuntimeException -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php deleted file mode 100644 index 3d2626e04..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/NotSupportedException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class NotSupportedException extends RuntimeException implements ExceptionInterface -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php deleted file mode 100644 index a6aa51740..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/RuntimeException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php deleted file mode 100644 index 720c9e78a..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Exception/TargetLocatorException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Exception; - -class TargetLocatorException extends RuntimeException -{ - private $resource; - - public function __construct($resource, $message, $code = 0, $previous = null) - { - $this->resource = $resource; - parent::__construct($message, $code, $previous); - } - - public function getResource() - { - return $this->resource; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php deleted file mode 100644 index 7d6cf209e..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/FileStrategyInterface.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -interface FileStrategyInterface -{ - /** - * Returns an array of adapters that match the strategy - * - * @return array - */ - public function getAdapters(); - - /** - * Returns the file extension that match the strategy - * - * @return string - */ - public function getFileExtension(); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php deleted file mode 100644 index b63562bd9..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TB2FileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TB2FileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tb2'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php deleted file mode 100644 index c3295b8ff..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TBz2FileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TBz2FileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tbz2'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php deleted file mode 100644 index 7ace0a399..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TGzFileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TGzFileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tgz'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php deleted file mode 100644 index 13ab29531..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarBz2FileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TarBz2FileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarBz2GNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBz2BSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tar.bz2'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php deleted file mode 100644 index 930ea45e8..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarFileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TarFileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarBSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tar'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php deleted file mode 100644 index 5dae40b32..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/TarGzFileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class TarGzFileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\GNUTar\\TarGzGNUTarAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\BSDTar\\TarGzBSDTarAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'tar.gz'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php deleted file mode 100644 index 312bfb57a..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/FileStrategy/ZipFileStrategy.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\FileStrategy; - -use Alchemy\Zippy\Adapter\AdapterContainer; - -class ZipFileStrategy implements FileStrategyInterface -{ - private $container; - - public function __construct(AdapterContainer $container) - { - $this->container = $container; - } - - /** - * {@inheritdoc} - */ - public function getAdapters() - { - return array( - $this->container['Alchemy\\Zippy\\Adapter\\ZipAdapter'], - $this->container['Alchemy\\Zippy\\Adapter\\ZipExtensionAdapter'], - ); - } - - /** - * {@inheritdoc} - */ - public function getFileExtension() - { - return 'zip'; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php deleted file mode 100644 index 364546ed3..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/BSDTarOutputParser.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Alchemy\Zippy\Parser; - -use Alchemy\Zippy\Exception\RuntimeException; - -/** - * This class is responsible of parsing GNUTar command line output - */ -class BSDTarOutputParser implements ParserInterface -{ - const PERMISSIONS = "([ldrwx-]+)"; - const HARD_LINK = "(\d+)"; - const OWNER = "([a-z][-a-z0-9]*)"; - const GROUP = "([a-z][-a-z0-9]*)"; - const FILESIZE = "(\d*)"; - const DATE = "([a-zA-Z0-9]+\s+[a-z0-9]+\s+[a-z0-9:]+)"; - const FILENAME = "(.*)"; - - /** - * @inheritdoc - */ - public function parseFileListing($output) - { - $lines = array_values(array_filter(explode("\n", $output))); - $members = array(); - - // BSDTar outputs two differents format of date according to the mtime - // of the member. If the member is younger than six months the year is not shown. - // On 4.5+ FreeBSD system the day is displayed first - $dateFormats = array('M d Y', 'M d H:i', 'd M Y', 'd M H:i'); - - foreach ($lines as $line) { - $matches = array(); - - // drw-rw-r-- 0 toto titi 0 Jan 3 1980 practice/ - // -rw-rw-r-- 0 toto titi 10240 Jan 22 13:31 practice/records - if (!preg_match_all("#" . - self::PERMISSIONS . "\s+" . // match (drw-r--r--) - self::HARD_LINK . "\s+" . // match (1) - self::OWNER . "\s" . // match (toto) - self::GROUP . "\s+" . // match (titi) - self::FILESIZE . "\s+" . // match (0) - self::DATE . "\s+" . // match (Jan 3 1980) - self::FILENAME . // match (practice) - "#", $line, $matches, PREG_SET_ORDER - )) { - continue; - } - - $chunks = array_shift($matches); - - if (8 !== count($chunks)) { - continue; - } - - $date = null; - - foreach ($dateFormats as $format) { - $date = \DateTime::createFromFormat($format, $chunks[6]); - - if (false === $date) { - continue; - } else { - break; - } - } - - if (false === $date) { - throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line)); - } - - $members[] = array( - 'location' => $chunks[7], - 'size' => $chunks[5], - 'mtime' => $date, - 'is_dir' => 'd' === $chunks[1][0] - ); - } - - return $members; - } - - /** - * @inheritdoc - */ - public function parseInflatorVersion($output) - { - $chunks = explode(' ', $output, 3); - - if (2 > count($chunks)) { - return null; - } - - list($name, $version) = explode(' ', $output, 3); - - return $version; - } - - /** - * @inheritdoc - */ - public function parseDeflatorVersion($output) - { - return $this->parseInflatoVersion($output); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php deleted file mode 100644 index 2cd7127f4..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/GNUTarOutputParser.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Alchemy\Zippy\Parser; - -use Alchemy\Zippy\Exception\RuntimeException; - -/** - * This class is responsible of parsing GNUTar command line output - */ -class GNUTarOutputParser implements ParserInterface -{ - const PERMISSIONS = "([ldrwx-]+)"; - const OWNER = "([a-z][-a-z0-9]*)"; - const GROUP = "([a-z][-a-z0-9]*)"; - const FILESIZE = "(\d*)"; - const ISO_DATE = "([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)"; - const FILENAME = "(.*)"; - - /** - * @inheritdoc - */ - public function parseFileListing($output) - { - $lines = array_values(array_filter(explode("\n", $output))); - $members = array(); - - foreach ($lines as $line) { - $matches = array(); - - // -rw-r--r-- gray/staff 62373 2006-06-09 12:06 apple - if (!preg_match_all("#". - self::PERMISSIONS . "\s+" . // match (-rw-r--r--) - self::OWNER . "/" . // match (gray) - self::GROUP . "\s+" . // match (staff) - self::FILESIZE . "\s+" . // match (62373) - self::ISO_DATE . "\s+" . // match (2006-06-09 12:06) - self::FILENAME . // match (apple) - "#", - $line, $matches, PREG_SET_ORDER - )) { - continue; - } - - $chunks = array_shift($matches); - - if (7 !== count($chunks)) { - continue; - } - - $date = \DateTime::createFromFormat("Y-m-d H:i", $chunks[5]); - - if (false === $date) { - throw new RuntimeException(sprintf('Failed to parse mtime date from %s', $line)); - } - - $members[] = array( - 'location' => $chunks[6], - 'size' => $chunks[4], - 'mtime' => $date, - 'is_dir' => 'd' === $chunks[1][0] - ); - } - - return $members; - } - - /** - * @inheritdoc - */ - public function parseInflatorVersion($output) - { - $chunks = explode(' ', $output, 3); - - if (2 > count($chunks)) { - return null; - } - - list($name, $version) = $chunks; - - return $version; - } - - /** - * @inheritdoc - */ - public function parseDeflatorVersion($output) - { - return $this->parseInflatoVersion($output); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php deleted file mode 100644 index c515716b9..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserFactory.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Parser; - -use Alchemy\Zippy\Exception\InvalidArgumentException; - -class ParserFactory -{ - /** - * Maps the corresponding parser to the selected adapter - * - * @param $adapterName An adapter name - * - * @return ParserInterface - * - * @throws InvalidArgumentException In case no parser were found - */ - public static function create($adapterName) - { - switch ($adapterName) { - case 'gnu-tar': - return new GNUTarOutputParser(); - break; - case 'bsd-tar': - return new BSDTarOutputParser(); - break; - case 'zip': - return new ZipOutputParser(); - break; - - default: - throw new InvalidArgumentException(sprintf('No parser available for %s adapter', $adapterName)); - break; - } - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php deleted file mode 100644 index 3e25e6c8f..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ParserInterface.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Parser; - -use Alchemy\Zippy\Exception\RuntimeException; - -interface ParserInterface -{ - /** - * Parses a file listing - * - * @param String $output The string to parse - * - * @return Array An array of Member properties (location, mtime, size & is_dir) - * - * @throws RuntimeException In case the parsing process failed - */ - public function parseFileListing($output); - - /** - * Parses the inflator binary version - * - * @param String $output - * - * @return String The version - */ - public function parseInflatorVersion($output); - - /** - * Parses the deflator binary version - * - * @param String $output - * - * @return String The version - */ - public function parsedeflatorVersion($output); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php deleted file mode 100644 index 507501a48..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Parser/ZipOutputParser.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace Alchemy\Zippy\Parser; - -/** - * This class is responsible of parsing GNUTar command line output - */ -class ZipOutputParser implements ParserInterface -{ - const LENGTH = "(\d*)"; - const ISO_DATE = "([0-9]+-[0-9]+-[0-9]+\s+[0-9]+:[0-9]+)"; - const FILENAME = "(.*)"; - - /** - * @inheritdoc - */ - public function parseFileListing($output) - { - $lines = array_values(array_filter(explode("\n", $output))); - $members = array(); - - foreach ($lines as $line) { - $matches = array(); - - // 785 2012-10-24 10:39 file - if (!preg_match_all("#". - self::LENGTH . "\s+" . // match (785) - self::ISO_DATE . "\s+" . // match (2012-10-24 10:39) - self::FILENAME . // match (file) - "#", - $line, $matches, PREG_SET_ORDER - )) { - continue; - } - - $chunks = array_shift($matches); - - if (4 !== count($chunks)) { - continue; - } - - $members[] = array( - 'location' => $chunks[3], - 'size' => $chunks[1], - 'mtime' => \DateTime::createFromFormat("Y-m-d H:i", $chunks[2]), - 'is_dir' => '/' === substr($chunks[3], -1) - ); - } - - return $members; - } - - /** - * @inheritdoc - */ - public function parseInflatorVersion($output) - { - $lines = array_values(array_filter(explode("\n", $output, 3))); - - $chunks = explode(' ', $lines[1], 3); - - if (2 > count($chunks)) { - return null; - } - - list($name, $version) = $chunks; - - return $version; - } - - /** - * @inheritdoc - */ - public function parseDeflatorVersion($output) - { - $lines = array_values(array_filter(explode("\n", $output, 2))); - $firstLine = array_shift($lines); - $chunks = explode(' ', $firstLine, 3); - - if (2 > count($chunks)) { - return null; - } - - list($name, $version) = $chunks; - - return $version; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php deleted file mode 100644 index f6aa18a5f..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactory.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\ProcessBuilder; - -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Symfony\Component\Process\ProcessBuilder; - -class ProcessBuilderFactory implements ProcessBuilderFactoryInterface -{ - /** - * The binary path - * - * @var String - */ - protected $binary; - - /** - * Constructor - * - * @param String $binary The path to the binary - * - * @throws InvalidArgumentException In case binary path is invalid - */ - public function __construct($binary) - { - $this->useBinary($binary); - } - - /** - * @inheritdoc - */ - public function getBinary() - { - return $this->binary; - } - - /** - * @inheritdoc - */ - public function useBinary($binary) - { - if (!is_executable($binary)) { - throw new InvalidArgumentException(sprintf('`%s` is not an executable binary', $binary)); - } - - $this->binary = $binary; - - return $this; - } - - /** - * @inheritdoc - */ - public function create() - { - if (null === $this->binary) { - throw new InvalidArgumentException('No binary set'); - } - - return ProcessBuilder::create(array($this->binary))->setTimeout(null); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php deleted file mode 100644 index f8149a9a4..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/ProcessBuilder/ProcessBuilderFactoryInterface.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\ProcessBuilder; - -use Symfony\Component\Process\ProcessBuilder; -use Alchemy\Zippy\Exception\InvalidArgumentException; - -interface ProcessBuilderFactoryInterface -{ - /** - * Returns a new instance of Symfony ProcessBuilder - * - * @return ProcessBuilder - * - * @throws InvalidArgumentException - */ - public function create(); - - /** - * Returns the binary path - * - * @return String - */ - public function getBinary(); - - /** - * Sets the binary path - * - * @param String $binary A binary path - * - * @return ProcessBuilderFactoryInterface - * - * @throws InvalidArgumentException In case binary is not executable - */ - public function useBinary($binary); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php deleted file mode 100644 index 9669089c7..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/RequestMapper.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -class RequestMapper -{ - private $locator; - - /** - * Constructor - * - * @param TargetLocator $locator - */ - public function __construct(TargetLocator $locator) - { - $this->locator = $locator; - } - - /** - * Maps resources request to a ResourceCollection - * - * @return ResourceCollection - */ - public function map($context, array $resources) - { - $data = array(); - - foreach ($resources as $location => $resource) { - if (is_int($location)) { - $data[] = new Resource($resource, $this->locator->locate($context, $resource)); - } else { - $data[] = new Resource($resource, ltrim($location, '/')); - } - } - - if (count($data) === 1) { - $context = $data[0]->getOriginal(); - } - - $collection = new ResourceCollection($context, $data, false); - - return $collection; - } - - /** - * Creates the default RequestMapper - * - * @return RequestMapper - */ - public static function create() - { - return new static(new TargetLocator()); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php deleted file mode 100644 index 11b0ae4e0..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Resource.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -class Resource -{ - private $original; - private $target; - - /** - * Constructor - * - * @param String $original - * @param String $target - */ - public function __construct($original, $target) - { - $this->original = $original; - $this->target = $target; - } - - /** - * Returns the target - * - * @return String - */ - public function getTarget() - { - return $this->target; - } - - /** - * Returns the original path - * - * @return String - */ - public function getOriginal() - { - return $this->original; - } - - /** - * Returns whether the resource can be processed in place given a context or not. - * - * For example : - * - /path/to/file1 can be processed to file1 in /path/to context - * - /path/to/subdir/file2 can be processed to subdir/file2 in /path/to context - * - * @param String $context - * - * @return Boolean - */ - public function canBeProcessedInPlace($context) - { - if (!is_string($this->original)) { - return false; - } - - if (!$this->isLocal()) { - return false; - } - - $data = parse_url($this->original); - - return sprintf('%s/%s', rtrim($context, '/'), $this->target) === $data['path']; - } - - /** - * Returns a context for computing this resource in case it is possible. - * - * Useful to avoid teleporting. - * - * @return null|string - */ - public function getContextForProcessInSinglePlace() - { - if (!$this->isLocal()) { - return null; - } - - if (basename($this->original) === $this->target) { - return dirname($this->original); - } - } - - /** - * Returns true if the resource is local. - * - * @return Boolean - */ - private function isLocal() - { - $data = parse_url($this->original); - - return isset($data['path']); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php deleted file mode 100644 index ab69044d1..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceCollection.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Doctrine\Common\Collections\ArrayCollection; - -class ResourceCollection extends ArrayCollection -{ - private $context; - private $temporary; - - /** - * Constructor - * - * @param String $context - * @param Resource[] $elements An array of Resource - */ - public function __construct($context, array $elements, $temporary) - { - array_walk($elements, function ($element) { - if (!$element instanceof Resource) { - throw new InvalidArgumentException('ResourceCollection only accept Resource elements'); - } - }); - - $this->context = $context; - $this->temporary = (Boolean) $temporary; - parent::__construct($elements); - } - - /** - * Returns the context related to the collection - * - * @return String - */ - public function getContext() - { - return $this->context; - } - - /** - * Tells whether the collection is temporary or not. - * - * A ResourceCollection is temporary when it required a temporary folder to - * fetch data - * - * @return type - */ - public function isTemporary() - { - return $this->temporary; - } - - /** - * Returns true if all resources can be processed in place, false otherwise - * - * @return Boolean - */ - public function canBeProcessedInPlace() - { - if (count($this) === 1) { - if (null !== $context = $this->first()->getContextForProcessInSinglePlace()) { - $this->context = $context; - return true; - } - } - - foreach ($this as $resource) { - if (!$resource->canBeProcessedInPlace($this->context)) { - return false; - } - } - - return true; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php deleted file mode 100644 index be93ef4ce..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceManager.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -use Alchemy\Zippy\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Filesystem\Exception\IOException as SfIOException; - -class ResourceManager -{ - private $mapper; - private $teleporter; - private $filesystem; - - /** - * Constructor - * - * @param RequestMapper $mapper - * @param ResourceTeleporter $teleporter - * @param Filesystem $filesystem - */ - public function __construct(RequestMapper $mapper, ResourceTeleporter $teleporter, Filesystem $filesystem) - { - $this->mapper = $mapper; - $this->filesystem = $filesystem; - $this->teleporter = $teleporter; - } - - /** - * Handles an archival request. - * - * The request is an array of string|streams to compute in a context (current - * working directory most of the time) - * Some keys can be associative. In these cases, the key is used the target - * for the file. - * - * @param String $context - * @param Array $request - * - * @return ResourceCollection - * - * @throws IOException In case of write failure - */ - public function handle($context, array $request) - { - $collection = $this->mapper->map($context, $request); - - if (!$collection->canBeProcessedInPlace()) { - $context = sprintf('%s/%s', sys_get_temp_dir(), uniqid('zippy_')); - - try { - $this->filesystem->mkdir($context); - } catch (SfIOException $e) { - throw new IOException(sprintf('Could not create temporary folder %s', $context), $e->getCode(), $e); - } - - foreach ($collection as $resource) { - $this->teleporter->teleport($context, $resource); - } - - $collection = new ResourceCollection($context, $collection->toArray(), true); - } - - return $collection; - } - - /** - * This method must be called once the ResourceCollection has been processed. - * - * It will remove temporary files - * - * @todo this should be done in the __destruct method of ResourceCollection - * - * @param ResourceCollection $collection - */ - public function cleanup(ResourceCollection $collection) - { - if ($collection->isTemporary()) { - try { - $this->filesystem->remove($collection->getContext()); - } catch (IOException $e) { - // log this ? - } - } - } - - /** - * Creates a default ResourceManager - * - * @return ResourceManager - */ - public static function create() - { - return new static(RequestMapper::create(), ResourceTeleporter::create(), new Filesystem()); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php deleted file mode 100644 index 362a85187..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/ResourceTeleporter.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -class ResourceTeleporter -{ - private $container; - - /** - * Constructor - * - * @param TeleporterContainer $container - */ - public function __construct(TeleporterContainer $container) - { - $this->container = $container; - } - - /** - * Teleports a Resource to its target in the context - * - * @param String $context - * @param Resource $resource - * - * @return ResourceTeleporter - */ - public function teleport($context, Resource $resource) - { - $this - ->container - ->fromResource($resource) - ->teleport($resource, $context); - - return $this; - } - - /** - * Creates the ResourceTeleporter with the default TeleporterContainer - * - * @return ResourceTeleporter - */ - public static function create() - { - return new static(TeleporterContainer::load()); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php deleted file mode 100644 index ed8e2df10..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TargetLocator.php +++ /dev/null @@ -1,156 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -use Alchemy\Zippy\Exception\TargetLocatorException; - -class TargetLocator -{ - /** - * Locates the target for a resource in a context - * - * For example, adding /path/to/file where the context (current working - * directory) is /path/to will return `file` as target - * - * @param String $context - * @param String|resource $resource - * - * @return String - * - * @throws TargetLocatorException In case the resource is invalid - */ - public function locate($context, $resource) - { - switch (true) { - case is_resource($resource): - return $this->locateResource($resource); - case is_string($resource): - return $this->locateString($context, $resource); - case $resource instanceof \SplFileInfo: - return $this->locateString($context, $resource->getRealpath()); - default: - throw new TargetLocatorException($resource, 'Unknown resource format'); - } - } - - /** - * Locate the target for a resource. - * - * @param resource $resource - * - * @return String - * - * @throws TargetLocatorException - */ - private function locateResource($resource) - { - $meta = stream_get_meta_data($resource); - $data = parse_url($meta['uri']); - - if (!isset($data['path'])) { - throw new TargetLocatorException($resource, 'Unable to retrieve path from resource'); - } - - return basename($data['path']); - } - - /** - * Locate the target for a string. - * - * @param String $resource - * - * @return String - * - * @throws TargetLocatorException - */ - private function locateString($context, $resource) - { - $url = parse_url($resource); - - if (isset($url['scheme']) && $this->isLocalFilesystem($url['scheme'])) { - $resource = $url['path'] = $this->cleanupPath($url['path']); - } - - // resource is a URI - if (isset($url['scheme'])) { - if ($this->isLocalFilesystem($url['scheme']) && $this->isFileInContext($url['path'], $context)) { - return $this->getRelativePathFromContext($url['path'], $context); - } - - return basename($resource); - } - - // resource is a local path - if ($this->isFileInContext($resource, $context)) { - $resource = $this->cleanupPath($resource); - - return $this->getRelativePathFromContext($resource, $context); - } else { - return basename($resource); - } - } - - /** - * Removes backward path sequences (..) - * - * @param String $path - * - * @return String - * - * @throws TargetLocatorException In case the path is invalid - */ - private function cleanupPath($path) - { - if (false === $cleanPath = realpath($path)) { - throw new TargetLocatorException($path, sprintf('%s is an invalid location', $path)); - } - - return $cleanPath; - } - - /** - * Checks whether the path belong to the context - * - * @param String $path A resource path - * @param String $context - * - * @return Boolean - */ - private function isFileInContext($path, $context) - { - return 0 === strpos($path, $context); - } - - /** - * Gets the relative path from the context for the given path - * - * @param String $path A resource path - * - * @return String - */ - private function getRelativePathFromContext($path, $context) - { - return ltrim(str_replace($context, '', $path), '/\\'); - } - - /** - * Checks if a scheme reffers to a local filesystem - * - * @param String $scheme - * - * @return Boolean - */ - private function isLocalFilesystem($scheme) - { - return 'plainfile' === $scheme || 'file' === $scheme; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php deleted file mode 100644 index ee129ec16..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/AbstractTeleporter.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource\Teleporter; - -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Exception\IOException; - -abstract class AbstractTeleporter implements TeleporterInterface -{ - /** - * Writes the target - * - * @param String $data - * @param Resource $resource - * @param String $context - * - * @return TeleporterInterface - * - * @throws IOException - */ - protected function writeTarget($data, Resource $resource, $context) - { - $target = $this->getTarget($context, $resource); - - if (false === file_put_contents($target, $data)) { - throw new IOException(sprintf('Could not write to %s', $target)); - } - - return $this; - } - - /** - * Returns the relative target of a Resource - * - * @param String $context - * @param Resource $resource - * - * @return String - */ - protected function getTarget($context, Resource $resource) - { - return sprintf('%s/%s', rtrim($context, '/'), $resource->getTarget()); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php deleted file mode 100644 index 0133c0728..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/GuzzleTeleporter.php +++ /dev/null @@ -1,74 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource\Teleporter; - -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Exception\RuntimeException; -use Guzzle\Http\Client; -use Guzzle\Plugin\Backoff\BackoffPlugin; -use Guzzle\Common\Event; - -/** - * Guzzle Teleporter implementation for HTTP resources - */ -class GuzzleTeleporter extends AbstractTeleporter -{ - private $client; - - /** - * Constructor - * - * @param Client $client - */ - public function __construct(Client $client) - { - $this->client = $client; - } - - /** - * {@inheritdoc} - */ - public function teleport(Resource $resource, $context) - { - $target = $this->getTarget($context, $resource); - - $stream = fopen($target, 'w'); - $response = $this->client->get($resource->getOriginal(), null, $stream)->send(); - fclose($stream); - - if (!$response->isSuccessful()) { - throw new RuntimeException(sprintf('Unable to fetch %s', $resource->getOriginal())); - } - - return $this; - } - - /** - * Creates the GuzzleTeleporter - * - * @return GuzzleTeleporter - */ - public static function create() - { - $client = new Client(); - - $client->getEventDispatcher()->addListener('request.error', function (Event $event) { - // override guzzle default behavior of throwing exceptions - // when 4xx & 5xx responses are encountered - $event->stopPropagation(); - }, -254); - - $client->addSubscriber(BackoffPlugin::getExponentialBackoff(5, array(500, 502, 503, 408))); - - return new static($client); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php deleted file mode 100644 index 89674ec83..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/LocalTeleporter.php +++ /dev/null @@ -1,71 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource\Teleporter; - -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Exception\IOException; -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Symfony\Component\Filesystem\Filesystem; -use Symfony\Component\Filesystem\Exception\IOException as SfIOException; - -/** - * This class transport an object using the local filesystem - */ -class LocalTeleporter extends AbstractTeleporter -{ - private $filesystem; - - /** - * Constructor - * - * @param Filesystem $filesystem - */ - public function __construct(Filesystem $filesystem) - { - $this->filesystem = $filesystem; - } - - /** - * {@inheritdoc} - */ - public function teleport(Resource $resource, $context) - { - $target = $this->getTarget($context, $resource); - $path = $resource->getOriginal(); - - if (!file_exists($path)) { - throw new InvalidArgumentException(sprintf('Invalid path %s', $path)); - } - - try { - if (is_file($path)) { - $this->filesystem->copy($path, $target); - } elseif (is_dir($path)) { - $this->filesystem->mirror($path, $target); - } else { - throw new InvalidArgumentException(sprintf('Invalid file or directory %s', $path)); - } - } catch (SfIOException $e) { - throw new IOException(sprintf('Could not write %s', $target), $e->getCode(), $e); - } - } - - /** - * Creates the LocalTeleporter - * - * @return LocalTeleporter - */ - public static function create() - { - return new static(new Filesystem()); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php deleted file mode 100644 index 4150f4052..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/StreamTeleporter.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource\Teleporter; - -use Alchemy\Zippy\Resource\Resource; -use Alchemy\Zippy\Exception\InvalidArgumentException; - -/** - * This class transport an object using php stream wrapper - */ -class StreamTeleporter extends AbstractTeleporter -{ - /** - * {@inheritdoc} - */ - public function teleport(Resource $resource, $context) - { - $streamCreated = false; - - if (is_resource($resource->getOriginal())) { - $stream = $resource->getOriginal(); - } else { - $stream = @fopen($resource->getOriginal(), 'rb'); - - if (!is_resource($stream)) { - throw new InvalidArgumentException(sprintf( - 'The stream or file "%s" could not be opened: ', - $resource->getOriginal() - )); - } - $streamCreated = true; - } - - $content = stream_get_contents($stream); - - if ($streamCreated) { - fclose($stream); - } - - $this->writeTarget($content, $resource, $context); - } - - /** - * Creates the StreamTeleporter - * - * @return StreamTeleporter - */ - public static function create() - { - return new static(); - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php deleted file mode 100644 index 6f4012be3..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/Teleporter/TeleporterInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource\Teleporter; - -use Alchemy\Zippy\Resource\Resource; - -interface TeleporterInterface -{ - /** - * Teleport a file from a destination to an other - * - * @param Resource $resource A Resource - * @param string $context The current context - * - * @throws IOException In case file could not be written on local - * @throws InvalidArgumentException In case path to file is not valid - */ - public function teleport(Resource $resource, $context); -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php deleted file mode 100644 index 3109a2497..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Resource/TeleporterContainer.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy\Resource; - -use Alchemy\Zippy\Exception\InvalidArgumentException; -use Alchemy\Zippy\Resource\Teleporter\LocalTeleporter; -use Alchemy\Zippy\Resource\Teleporter\GuzzleTeleporter; -use Alchemy\Zippy\Resource\Teleporter\StreamTeleporter; -use Alchemy\Zippy\Resource\Teleporter\TeleporterInterface; - -/** - * A container of TeleporterInterface - */ -class TeleporterContainer extends \Pimple -{ - /** - * Returns the appropriate TeleporterInterface given a Resource - * - * @param Resource $resource - * - * @return TeleporterInterface - * - * @throws InvalidArgumentException - */ - public function fromResource(Resource $resource) - { - switch (true) { - case is_resource($resource->getOriginal()): - $teleporter = 'stream-teleporter'; - break; - case is_string($resource->getOriginal()): - $data = parse_url($resource->getOriginal()); - - if (!isset($data['scheme']) || 'file' === $data['scheme']) { - $teleporter = 'local-teleporter'; - } elseif (in_array($data['scheme'], array('http', 'https'))) { - $teleporter = 'guzzle-teleporter'; - } else { - $teleporter = 'stream-teleporter'; - } - break; - default: - throw new InvalidArgumentException('No teleporter found'); - } - - return $this[$teleporter]; - } - - /** - * Instantiates TeleporterContainer and register default teleporters - * - * @return TeleporterContainer - */ - public static function load() - { - $container = new static(); - - $container['stream-teleporter'] = $container->share(function () { - return StreamTeleporter::create(); - }); - $container['local-teleporter'] = $container->share(function () { - return LocalTeleporter::create(); - }); - $container['guzzle-teleporter'] = $container->share(function () { - return GuzzleTeleporter::create(); - }); - - return $container; - } -} diff --git a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php b/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php deleted file mode 100644 index 60fb5969f..000000000 --- a/core/lib/alchemy/zippy/src/Alchemy/Zippy/Zippy.php +++ /dev/null @@ -1,213 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Alchemy\Zippy; - -use Alchemy\Zippy\Adapter\AdapterInterface; -use Alchemy\Zippy\Adapter\AdapterContainer; -use Alchemy\Zippy\Archive\ArchiveInterface; -use Alchemy\Zippy\Exception\ExceptionInterface; -use Alchemy\Zippy\Exception\FormatNotSupportedException; -use Alchemy\Zippy\Exception\NoAdapterOnPlatformException; -use Alchemy\Zippy\Exception\RuntimeException; -use Alchemy\Zippy\FileStrategy\FileStrategyInterface; -use Alchemy\Zippy\FileStrategy\TarFileStrategy; -use Alchemy\Zippy\FileStrategy\TarBz2FileStrategy; -use Alchemy\Zippy\FileStrategy\TarGzFileStrategy; -use Alchemy\Zippy\FileStrategy\TB2FileStrategy; -use Alchemy\Zippy\FileStrategy\TBz2FileStrategy; -use Alchemy\Zippy\FileStrategy\TGzFileStrategy; -use Alchemy\Zippy\FileStrategy\ZipFileStrategy; - -class Zippy -{ - public $adapters; - private $strategies = array(); - - public function __construct(AdapterContainer $adapters) - { - $this->adapters = $adapters; - } - - /** - * Creates an archive - * - * @param string $path - * @param String|Array|\Traversable|null $files - * @param Boolean $recursive - * @param string|null $type - * - * @return ArchiveInterface - * - * @throws RuntimeException In case of failure - */ - public function create($path, $files = null, $recursive = true, $type = null) - { - if (null === $type) { - $type = $this->guessAdapterExtension($path); - } - - try { - return $this - ->getAdapterFor($this->sanitizeExtension($type)) - ->create($path, $files, $recursive); - } catch (ExceptionInterface $e) { - throw new RuntimeException('Unable to create archive', $e->getCode(), $e); - } - } - - /** - * Opens an archive. - * - * @param string $path - * - * @return ArchiveInterface - * - * @throws RuntimeException In case of failure - */ - public function open($path) - { - $type = $this->guessAdapterExtension($path); - - try { - return $this - ->getAdapterFor($this->sanitizeExtension($type)) - ->open($path); - } catch (ExceptionInterface $e) { - throw new RuntimeException('Unable to open archive', $e->getCode(), $e); - } - } - - /** - * Adds a strategy. - * - * The last strategy added is preferred over the other ones. - * You can add a strategy twice ; when doing this, the first one is removed - * when inserting the second one. - * - * @param FileStrategyInterface $strategy - * - * @return Zippy - */ - public function addStrategy(FileStrategyInterface $strategy) - { - $extension = $this->sanitizeExtension($strategy->getFileExtension()); - - if (!isset($this->strategies[$extension])) { - $this->strategies[$extension] = array(); - } - - if (false !== $key = array_search($strategy, $this->strategies[$extension], true)) { - unset($this->strategies[$extension][$key]); - } - - array_unshift($this->strategies[$extension], $strategy); - - return $this; - } - - /** - * Returns the strategies as they are stored - * - * @return array - */ - public function getStrategies() - { - return $this->strategies; - } - - /** - * Returns an adapter for a file extension - * - * @param string $extension The extension - * - * @return AdapterInterface - * - * @throws FormatNotSupportedException When no strategy is defined for this extension - * @throws NoAdapterOnPlatformException When no adapter is supported for this extension on this platform - */ - public function getAdapterFor($extension) - { - $extension = $this->sanitizeExtension($extension); - - if (!$extension || !isset($this->strategies[$extension])) { - throw new FormatNotSupportedException(sprintf('No strategy for %s extension', $extension)); - } - - foreach ($this->strategies[$extension] as $strategy) { - foreach ($strategy->getAdapters() as $adapter) { - if ($adapter->isSupported()) { - return $adapter; - } - } - } - - throw new NoAdapterOnPlatformException(sprintf('No adapter available for %s on this platform', $extension)); - } - - /** - * Creates Zippy and loads default strategies - * - * @return Zippy - */ - public static function load() - { - $adapters = AdapterContainer::load(); - $factory = new static($adapters); - - $factory->addStrategy(new ZipFileStrategy($adapters)); - $factory->addStrategy(new TarFileStrategy($adapters)); - $factory->addStrategy(new TarGzFileStrategy($adapters)); - $factory->addStrategy(new TarBz2FileStrategy($adapters)); - $factory->addStrategy(new TB2FileStrategy($adapters)); - $factory->addStrategy(new TBz2FileStrategy($adapters)); - $factory->addStrategy(new TGzFileStrategy($adapters)); - - return $factory; - } - - /** - * Sanitize an extension. - * - * Strips dot from the beginning, converts to lowercase and remove trailing - * whitespaces - * - * @param string $extension - * - * @return string - */ - private function sanitizeExtension($extension) - { - return ltrim(trim(mb_strtolower($extension)), '.'); - } - - /** - * Finds an extension that has strategy registered given a file path - * - * Returns null if no matching strategy found. - * - * @param string $path - * - * @return string|null - */ - private function guessAdapterExtension($path) - { - $path = strtolower(trim($path)); - - foreach ($this->strategies as $extension => $strategy) { - if ($extension === substr($path, (strlen($extension) * -1))) { - return $extension; - } - } - - return null; - } -} diff --git a/core/lib/autoload.php b/core/lib/autoload.php deleted file mode 100644 index 5e8f5e9e9..000000000 --- a/core/lib/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0 class loader - * - * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - */ -class ClassLoader -{ - // PSR-4 - private $prefixLengthsPsr4 = array(); - private $prefixDirsPsr4 = array(); - private $fallbackDirsPsr4 = array(); - - // PSR-0 - private $prefixesPsr0 = array(); - private $fallbackDirsPsr0 = array(); - - private $useIncludePath = false; - private $classMap = array(); - - public function getPrefixes() - { - return call_user_func_array('array_merge', $this->prefixesPsr0); - } - - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - */ - public function add($prefix, $paths, $prepend = false) - { - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - (array) $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - (array) $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-0 base directories - * @param bool $prepend Whether to prepend the directories - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - (array) $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - (array) $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - (array) $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - */ - public function setPsr4($prefix, $paths) { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - } - - /** - * Unregisters this instance as an autoloader. - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - includeFile($file); - - return true; - } - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 - if ('\\' == $class[0]) { - $class = substr($class, 1); - } - - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { - if (0 === strpos($class, $prefix)) { - foreach ($this->prefixDirsPsr4[$prefix] as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - - // Remember that this class does not exist. - return $this->classMap[$class] = false; - } -} - -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; -} diff --git a/core/lib/composer/autoload_classmap.php b/core/lib/composer/autoload_classmap.php deleted file mode 100644 index 71dd9c179..000000000 --- a/core/lib/composer/autoload_classmap.php +++ /dev/null @@ -1,9 +0,0 @@ - array($vendorDir . '/symfony/yaml'), - 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), - 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), - 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), - 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), - 'Scan\\Kss' => array($vendorDir . '/scan/kss-php/lib'), - 'Pimple' => array($vendorDir . '/pimple/pimple/lib'), - 'Michelf' => array($vendorDir . '/michelf/php-markdown'), - 'Guzzle\\Tests' => array($vendorDir . '/guzzle/guzzle/tests'), - 'Guzzle' => array($vendorDir . '/guzzle/guzzle/src'), - 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), - 'Alchemy' => array($vendorDir . '/alchemy/zippy/src'), - '' => array($vendorDir), -); diff --git a/core/lib/composer/autoload_psr4.php b/core/lib/composer/autoload_psr4.php deleted file mode 100644 index 80607ee97..000000000 --- a/core/lib/composer/autoload_psr4.php +++ /dev/null @@ -1,9 +0,0 @@ - $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - - $loader->register(true); - - return $loader; - } -} - -function composerRequire30ec8539c8312241b2e6d5315067bda6($file) -{ - require $file; -} diff --git a/core/lib/composer/installed.json b/core/lib/composer/installed.json deleted file mode 100644 index 8f15061b2..000000000 --- a/core/lib/composer/installed.json +++ /dev/null @@ -1,643 +0,0 @@ -[ - { - "name": "symfony/filesystem", - "version": "v2.4.4", - "version_normalized": "2.4.4.0", - "target-dir": "Symfony/Component/Filesystem", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/a3af8294bcce4a7c1b2892363b0c9d8109affad4", - "reference": "a3af8294bcce4a7c1b2892363b0c9d8109affad4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2014-04-16 10:34:31", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com" - }, - { - "name": "symfony/process", - "version": "v2.4.4", - "version_normalized": "2.4.4.0", - "target-dir": "Symfony/Component/Process", - "source": { - "type": "git", - "url": "https://github.com/symfony/Process.git", - "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Process/zipball/8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", - "reference": "8721f1476d5d38a43c7d6ccb6435b351cf8f3bb7", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2014-04-27 13:34:57", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Process\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Process Component", - "homepage": "http://symfony.com" - }, - { - "name": "pimple/pimple", - "version": "v1.1.1", - "version_normalized": "1.1.1.0", - "source": { - "type": "git", - "url": "https://github.com/fabpot/Pimple.git", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/Pimple/zipball/2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "reference": "2019c145fe393923f3441b23f29bbdfaa5c58c4d", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2013-11-22 08:30:29", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Pimple": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - } - ], - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "homepage": "http://pimple.sensiolabs.org", - "keywords": [ - "container", - "dependency injection" - ] - }, - { - "name": "symfony/event-dispatcher", - "version": "v2.4.4", - "version_normalized": "2.4.4.0", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "e539602e5455aa086c0e81e604745af7789e4d8a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e539602e5455aa086c0e81e604745af7789e4d8a", - "reference": "e539602e5455aa086c0e81e604745af7789e4d8a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~2.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "time": "2014-04-16 10:34:31", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com" - }, - { - "name": "guzzle/guzzle", - "version": "v3.9.1", - "version_normalized": "3.9.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/guzzle3.git", - "reference": "92d9934f2fca1da15178c91239576ae26e505e60" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/92d9934f2fca1da15178c91239576ae26e505e60", - "reference": "92d9934f2fca1da15178c91239576ae26e505e60", - "shasum": "" - }, - "require": { - "ext-curl": "*", - "php": ">=5.3.3", - "symfony/event-dispatcher": "~2.1" - }, - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - "require-dev": { - "doctrine/cache": "~1.3", - "monolog/monolog": "~1.0", - "phpunit/phpunit": "3.7.*", - "psr/log": "~1.0", - "symfony/class-loader": "~2.1", - "zendframework/zend-cache": "2.*,<2.3", - "zendframework/zend-log": "2.*,<2.3" - }, - "time": "2014-05-07 17:04:22", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.8-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Guzzle": "src/", - "Guzzle\\Tests": "tests/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "client", - "curl", - "framework", - "http", - "http client", - "rest", - "web service" - ] - }, - { - "name": "doctrine/collections", - "version": "v1.2", - "version_normalized": "1.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/collections.git", - "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/b99c5c46c87126201899afe88ec490a25eedd6a2", - "reference": "b99c5c46c87126201899afe88ec490a25eedd6a2", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "time": "2014-02-03 23:07:43", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Doctrine\\Common\\Collections\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com", - "homepage": "http://www.jwage.com/", - "role": "Creator" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com", - "homepage": "http://www.instaclick.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "http://jmsyst.com", - "role": "Developer of wrapped JMSSerializerBundle" - } - ], - "description": "Collections Abstraction library", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "array", - "collections", - "iterator" - ] - }, - { - "name": "alchemy/zippy", - "version": "0.2.0", - "version_normalized": "0.2.0.0", - "source": { - "type": "git", - "url": "https://github.com/alchemy-fr/Zippy.git", - "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/alchemy-fr/Zippy/zipball/e652161e1d99f647b34c3ff9695bb4bb0b8837cf", - "reference": "e652161e1d99f647b34c3ff9695bb4bb0b8837cf", - "shasum": "" - }, - "require": { - "doctrine/collections": "~1.0", - "guzzle/guzzle": "~3.0", - "php": ">=5.3.3", - "pimple/pimple": "~1.0", - "symfony/filesystem": "~2.0", - "symfony/process": "~2.0" - }, - "require-dev": { - "ext-zip": "*", - "phpunit/phpunit": "~3.7", - "sami/sami": "dev-master@dev", - "symfony/finder": "~2.0" - }, - "suggest": { - "ext-zip": "To use the ZipExtensionAdapter" - }, - "time": "2014-04-04 16:24:46", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.2.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Alchemy": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alchemy", - "email": "dev.team@alchemy.fr", - "homepage": "http://www.alchemy.fr/" - } - ], - "description": "Zippy, the archive manager companion", - "keywords": [ - "bzip", - "compression", - "tar", - "zip" - ] - }, - { - "name": "michelf/php-markdown", - "version": "1.4.0", - "version_normalized": "1.4.0.0", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-markdown.git", - "reference": "96d8150406f67e683ef4acc09fef91785fef1266" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-markdown/zipball/96d8150406f67e683ef4acc09fef91785fef1266", - "reference": "96d8150406f67e683ef4acc09fef91785fef1266", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "time": "2013-11-29 17:09:24", - "type": "library", - "extra": { - "branch-alias": { - "dev-lib": "1.4.x-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "http://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "http://daringfireball.net/" - } - ], - "description": "PHP Markdown", - "homepage": "http://michelf.ca/projects/php-markdown/", - "keywords": [ - "markdown" - ] - }, - { - "name": "symfony/yaml", - "version": "v2.4.4", - "version_normalized": "2.4.4.0", - "target-dir": "Symfony/Component/Yaml", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/65539ecde838f9c0d18b006b2101e3deb4b5c9ff", - "reference": "65539ecde838f9c0d18b006b2101e3deb4b5c9ff", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2014-04-18 20:37:09", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com" - }, - { - "name": "symfony/finder", - "version": "v2.4.4", - "version_normalized": "2.4.4.0", - "target-dir": "Symfony/Component/Finder", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/25e1e7d5e7376f8a92ae3b1d714d956edf33a730", - "reference": "25e1e7d5e7376f8a92ae3b1d714d956edf33a730", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "time": "2014-04-27 13:34:57", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com", - "homepage": "http://fabien.potencier.org", - "role": "Lead Developer" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "http://symfony.com" - }, - { - "name": "scan/kss-php", - "version": "v0.6.0", - "version_normalized": "0.6.0.0", - "source": { - "type": "git", - "url": "https://github.com/scaninc/kss-php.git", - "reference": "5acddff2758761252d8e6da97680ef1f412457cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/scaninc/kss-php/zipball/5acddff2758761252d8e6da97680ef1f412457cb", - "reference": "5acddff2758761252d8e6da97680ef1f412457cb", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/finder": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*" - }, - "time": "2013-11-11 17:49:11", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Scan\\Kss": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Russell Ahlstrom", - "email": "russell.ahlstrom@gmail.com", - "homepage": "http://russell.ahlstromology.com" - } - ], - "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", - "keywords": [ - "css documentation", - "kss", - "styleguide" - ] - } -] diff --git a/core/lib/doctrine/collections/.gitignore b/core/lib/doctrine/collections/.gitignore deleted file mode 100644 index 48b8bf907..000000000 --- a/core/lib/doctrine/collections/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor/ diff --git a/core/lib/doctrine/collections/.travis.yml b/core/lib/doctrine/collections/.travis.yml deleted file mode 100644 index 470c9879a..000000000 --- a/core/lib/doctrine/collections/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - - hhvm - -before_script: - - composer --prefer-source --dev install diff --git a/core/lib/doctrine/collections/LICENSE b/core/lib/doctrine/collections/LICENSE deleted file mode 100644 index 5e781fce4..000000000 --- a/core/lib/doctrine/collections/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2006-2013 Doctrine Project - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/core/lib/doctrine/collections/README.md b/core/lib/doctrine/collections/README.md deleted file mode 100644 index 627d45bbb..000000000 --- a/core/lib/doctrine/collections/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# Doctrine Collections - -Collections Abstraction library - -## Changelog - -### v1.2 - -* Add a new ``AbstractLazyCollection`` - -### v1.1 - -* Deprecated ``Comparison::IS``, because it's only there for SQL semantics. - These are fixed in the ORM instead. -* Add ``Comparison::CONTAINS`` to perform partial string matches: - - $criteria->andWhere($criteria->expr()->contains('property', 'Foo')); diff --git a/core/lib/doctrine/collections/composer.json b/core/lib/doctrine/collections/composer.json deleted file mode 100644 index dd30961c8..000000000 --- a/core/lib/doctrine/collections/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "doctrine/collections", - "type": "library", - "description": "Collections Abstraction library", - "keywords": ["collections", "array", "iterator"], - "homepage": "http://www.doctrine-project.org", - "license": "MIT", - "authors": [ - {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, - {"name": "Roman Borschel", "email": "roman@code-factory.org"}, - {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, - {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, - {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} - ], - "require": { - "php": ">=5.3.2" - }, - "autoload": { - "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } - }, - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php deleted file mode 100644 index 0052a29c7..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php +++ /dev/null @@ -1,343 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -use Closure; - -/** - * Lazy collection that is backed by a concrete collection - * - * @author Michaël Gallego - * @since 1.2 - */ -abstract class AbstractLazyCollection implements Collection -{ - /** - * The backed collection to use - * - * @var Collection - */ - protected $collection; - - /** - * @var bool - */ - private $initialized = false; - - /** - * {@inheritDoc} - */ - public function count() - { - $this->initialize(); - return $this->collection->count(); - } - - /** - * {@inheritDoc} - */ - public function add($element) - { - $this->initialize(); - return $this->collection->add($element); - } - - /** - * {@inheritDoc} - */ - public function clear() - { - $this->initialize(); - $this->collection->clear(); - } - - /** - * {@inheritDoc} - */ - public function contains($element) - { - $this->initialize(); - return $this->collection->contains($element); - } - - /** - * {@inheritDoc} - */ - public function isEmpty() - { - $this->initialize(); - return $this->collection->isEmpty(); - } - - /** - * {@inheritDoc} - */ - public function remove($key) - { - $this->initialize(); - return $this->collection->remove($key); - } - - /** - * {@inheritDoc} - */ - public function removeElement($element) - { - $this->initialize(); - return $this->collection->removeElement($element); - } - - /** - * {@inheritDoc} - */ - public function containsKey($key) - { - $this->initialize(); - return $this->collection->containsKey($key); - } - - /** - * {@inheritDoc} - */ - public function get($key) - { - $this->initialize(); - return $this->collection->get($key); - } - - /** - * {@inheritDoc} - */ - public function getKeys() - { - $this->initialize(); - return $this->collection->getKeys(); - } - - /** - * {@inheritDoc} - */ - public function getValues() - { - $this->initialize(); - return $this->collection->getValues(); - } - - /** - * {@inheritDoc} - */ - public function set($key, $value) - { - $this->initialize(); - $this->collection->set($key, $value); - } - - /** - * {@inheritDoc} - */ - public function toArray() - { - $this->initialize(); - return $this->collection->toArray(); - } - - /** - * {@inheritDoc} - */ - public function first() - { - $this->initialize(); - return $this->collection->first(); - } - - /** - * {@inheritDoc} - */ - public function last() - { - $this->initialize(); - return $this->collection->last(); - } - - /** - * {@inheritDoc} - */ - public function key() - { - $this->initialize(); - return $this->collection->key(); - } - - /** - * {@inheritDoc} - */ - public function current() - { - $this->initialize(); - return $this->collection->current(); - } - - /** - * {@inheritDoc} - */ - public function next() - { - $this->initialize(); - return $this->collection->next(); - } - - /** - * {@inheritDoc} - */ - public function exists(Closure $p) - { - $this->initialize(); - return $this->collection->exists($p); - } - - /** - * {@inheritDoc} - */ - public function filter(Closure $p) - { - $this->initialize(); - return $this->collection->filter($p); - } - - /** - * {@inheritDoc} - */ - public function forAll(Closure $p) - { - $this->initialize(); - return $this->collection->forAll($p); - } - - /** - * {@inheritDoc} - */ - public function map(Closure $func) - { - $this->initialize(); - return $this->collection->map($func); - } - - /** - * {@inheritDoc} - */ - public function partition(Closure $p) - { - $this->initialize(); - return $this->collection->partition($p); - } - - /** - * {@inheritDoc} - */ - public function indexOf($element) - { - $this->initialize(); - return $this->collection->indexOf($element); - } - - /** - * {@inheritDoc} - */ - public function slice($offset, $length = null) - { - $this->initialize(); - return $this->collection->slice($offset, $length); - } - - /** - * {@inheritDoc} - */ - public function getIterator() - { - $this->initialize(); - return $this->collection->getIterator(); - } - - /** - * {@inheritDoc} - */ - public function offsetExists($offset) - { - $this->initialize(); - return $this->collection->offsetExists($offset); - } - - /** - * {@inheritDoc} - */ - public function offsetGet($offset) - { - $this->initialize(); - return $this->collection->offsetGet($offset); - } - - /** - * {@inheritDoc} - */ - public function offsetSet($offset, $value) - { - $this->initialize(); - $this->collection->offsetSet($offset, $value); - } - - /** - * {@inheritDoc} - */ - public function offsetUnset($offset) - { - $this->initialize(); - $this->collection->offsetUnset($offset); - } - - /** - * Is the lazy collection already initialized? - * - * @return bool - */ - public function isInitialized() - { - return $this->initialized; - } - - /** - * Initialize the collection - * - * @return void - */ - protected function initialize() - { - if (!$this->initialized) { - $this->doInitialize(); - $this->initialized = true; - } - } - - /** - * Do the initialization logic - * - * @return void - */ - abstract protected function doInitialize(); -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php deleted file mode 100644 index 9c2c8e147..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php +++ /dev/null @@ -1,385 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -use Closure, ArrayIterator; -use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; - -/** - * An ArrayCollection is a Collection implementation that wraps a regular PHP array. - * - * @since 2.0 - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - */ -class ArrayCollection implements Collection, Selectable -{ - /** - * An array containing the entries of this collection. - * - * @var array - */ - private $_elements; - - /** - * Initializes a new ArrayCollection. - * - * @param array $elements - */ - public function __construct(array $elements = array()) - { - $this->_elements = $elements; - } - - /** - * {@inheritDoc} - */ - public function toArray() - { - return $this->_elements; - } - - /** - * {@inheritDoc} - */ - public function first() - { - return reset($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function last() - { - return end($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function key() - { - return key($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function next() - { - return next($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function current() - { - return current($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function remove($key) - { - if (isset($this->_elements[$key]) || array_key_exists($key, $this->_elements)) { - $removed = $this->_elements[$key]; - unset($this->_elements[$key]); - - return $removed; - } - - return null; - } - - /** - * {@inheritDoc} - */ - public function removeElement($element) - { - $key = array_search($element, $this->_elements, true); - - if ($key !== false) { - unset($this->_elements[$key]); - - return true; - } - - return false; - } - - /** - * Required by interface ArrayAccess. - * - * {@inheritDoc} - */ - public function offsetExists($offset) - { - return $this->containsKey($offset); - } - - /** - * Required by interface ArrayAccess. - * - * {@inheritDoc} - */ - public function offsetGet($offset) - { - return $this->get($offset); - } - - /** - * Required by interface ArrayAccess. - * - * {@inheritDoc} - */ - public function offsetSet($offset, $value) - { - if ( ! isset($offset)) { - return $this->add($value); - } - return $this->set($offset, $value); - } - - /** - * Required by interface ArrayAccess. - * - * {@inheritDoc} - */ - public function offsetUnset($offset) - { - return $this->remove($offset); - } - - /** - * {@inheritDoc} - */ - public function containsKey($key) - { - return isset($this->_elements[$key]) || array_key_exists($key, $this->_elements); - } - - /** - * {@inheritDoc} - */ - public function contains($element) - { - return in_array($element, $this->_elements, true); - } - - /** - * {@inheritDoc} - */ - public function exists(Closure $p) - { - foreach ($this->_elements as $key => $element) { - if ($p($key, $element)) { - return true; - } - } - return false; - } - - /** - * {@inheritDoc} - */ - public function indexOf($element) - { - return array_search($element, $this->_elements, true); - } - - /** - * {@inheritDoc} - */ - public function get($key) - { - if (isset($this->_elements[$key])) { - return $this->_elements[$key]; - } - return null; - } - - /** - * {@inheritDoc} - */ - public function getKeys() - { - return array_keys($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function getValues() - { - return array_values($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function count() - { - return count($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function set($key, $value) - { - $this->_elements[$key] = $value; - } - - /** - * {@inheritDoc} - */ - public function add($value) - { - $this->_elements[] = $value; - return true; - } - - /** - * {@inheritDoc} - */ - public function isEmpty() - { - return ! $this->_elements; - } - - /** - * Required by interface IteratorAggregate. - * - * {@inheritDoc} - */ - public function getIterator() - { - return new ArrayIterator($this->_elements); - } - - /** - * {@inheritDoc} - */ - public function map(Closure $func) - { - return new static(array_map($func, $this->_elements)); - } - - /** - * {@inheritDoc} - */ - public function filter(Closure $p) - { - return new static(array_filter($this->_elements, $p)); - } - - /** - * {@inheritDoc} - */ - public function forAll(Closure $p) - { - foreach ($this->_elements as $key => $element) { - if ( ! $p($key, $element)) { - return false; - } - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function partition(Closure $p) - { - $coll1 = $coll2 = array(); - foreach ($this->_elements as $key => $element) { - if ($p($key, $element)) { - $coll1[$key] = $element; - } else { - $coll2[$key] = $element; - } - } - return array(new static($coll1), new static($coll2)); - } - - /** - * Returns a string representation of this object. - * - * @return string - */ - public function __toString() - { - return __CLASS__ . '@' . spl_object_hash($this); - } - - /** - * {@inheritDoc} - */ - public function clear() - { - $this->_elements = array(); - } - - /** - * {@inheritDoc} - */ - public function slice($offset, $length = null) - { - return array_slice($this->_elements, $offset, $length, true); - } - - /** - * {@inheritDoc} - */ - public function matching(Criteria $criteria) - { - $expr = $criteria->getWhereExpression(); - $filtered = $this->_elements; - - if ($expr) { - $visitor = new ClosureExpressionVisitor(); - $filter = $visitor->dispatch($expr); - $filtered = array_filter($filtered, $filter); - } - - if ($orderings = $criteria->getOrderings()) { - $next = null; - foreach (array_reverse($orderings) as $field => $ordering) { - $next = ClosureExpressionVisitor::sortByField($field, $ordering == 'DESC' ? -1 : 1, $next); - } - - usort($filtered, $next); - } - - $offset = $criteria->getFirstResult(); - $length = $criteria->getMaxResults(); - - if ($offset || $length) { - $filtered = array_slice($filtered, (int)$offset, $length); - } - - return new static($filtered); - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php deleted file mode 100644 index a0808b38c..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php +++ /dev/null @@ -1,260 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -use Closure, Countable, IteratorAggregate, ArrayAccess; - -/** - * The missing (SPL) Collection/Array/OrderedMap interface. - * - * A Collection resembles the nature of a regular PHP array. That is, - * it is essentially an ordered map that can also be used - * like a list. - * - * A Collection has an internal iterator just like a PHP array. In addition, - * a Collection can be iterated with external iterators, which is preferrable. - * To use an external iterator simply use the foreach language construct to - * iterate over the collection (which calls {@link getIterator()} internally) or - * explicitly retrieve an iterator though {@link getIterator()} which can then be - * used to iterate over the collection. - * You can not rely on the internal iterator of the collection being at a certain - * position unless you explicitly positioned it before. Prefer iteration with - * external iterators. - * - * @since 2.0 - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - */ -interface Collection extends Countable, IteratorAggregate, ArrayAccess -{ - /** - * Adds an element at the end of the collection. - * - * @param mixed $element The element to add. - * - * @return boolean Always TRUE. - */ - function add($element); - - /** - * Clears the collection, removing all elements. - * - * @return void - */ - function clear(); - - /** - * Checks whether an element is contained in the collection. - * This is an O(n) operation, where n is the size of the collection. - * - * @param mixed $element The element to search for. - * - * @return boolean TRUE if the collection contains the element, FALSE otherwise. - */ - function contains($element); - - /** - * Checks whether the collection is empty (contains no elements). - * - * @return boolean TRUE if the collection is empty, FALSE otherwise. - */ - function isEmpty(); - - /** - * Removes the element at the specified index from the collection. - * - * @param string|integer $key The kex/index of the element to remove. - * - * @return mixed The removed element or NULL, if the collection did not contain the element. - */ - function remove($key); - - /** - * Removes the specified element from the collection, if it is found. - * - * @param mixed $element The element to remove. - * - * @return boolean TRUE if this collection contained the specified element, FALSE otherwise. - */ - function removeElement($element); - - /** - * Checks whether the collection contains an element with the specified key/index. - * - * @param string|integer $key The key/index to check for. - * - * @return boolean TRUE if the collection contains an element with the specified key/index, - * FALSE otherwise. - */ - function containsKey($key); - - /** - * Gets the element at the specified key/index. - * - * @param string|integer $key The key/index of the element to retrieve. - * - * @return mixed - */ - function get($key); - - /** - * Gets all keys/indices of the collection. - * - * @return array The keys/indices of the collection, in the order of the corresponding - * elements in the collection. - */ - function getKeys(); - - /** - * Gets all values of the collection. - * - * @return array The values of all elements in the collection, in the order they - * appear in the collection. - */ - function getValues(); - - /** - * Sets an element in the collection at the specified key/index. - * - * @param string|integer $key The key/index of the element to set. - * @param mixed $value The element to set. - * - * @return void - */ - function set($key, $value); - - /** - * Gets a native PHP array representation of the collection. - * - * @return array - */ - function toArray(); - - /** - * Sets the internal iterator to the first element in the collection and returns this element. - * - * @return mixed - */ - function first(); - - /** - * Sets the internal iterator to the last element in the collection and returns this element. - * - * @return mixed - */ - function last(); - - /** - * Gets the key/index of the element at the current iterator position. - * - * @return int|string - */ - function key(); - - /** - * Gets the element of the collection at the current iterator position. - * - * @return mixed - */ - function current(); - - /** - * Moves the internal iterator position to the next element and returns this element. - * - * @return mixed - */ - function next(); - - /** - * Tests for the existence of an element that satisfies the given predicate. - * - * @param Closure $p The predicate. - * - * @return boolean TRUE if the predicate is TRUE for at least one element, FALSE otherwise. - */ - function exists(Closure $p); - - /** - * Returns all the elements of this collection that satisfy the predicate p. - * The order of the elements is preserved. - * - * @param Closure $p The predicate used for filtering. - * - * @return Collection A collection with the results of the filter operation. - */ - function filter(Closure $p); - - /** - * Tests whether the given predicate p holds for all elements of this collection. - * - * @param Closure $p The predicate. - * - * @return boolean TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. - */ - function forAll(Closure $p); - - /** - * Applies the given function to each element in the collection and returns - * a new collection with the elements returned by the function. - * - * @param Closure $func - * - * @return Collection - */ - function map(Closure $func); - - /** - * Partitions this collection in two collections according to a predicate. - * Keys are preserved in the resulting collections. - * - * @param Closure $p The predicate on which to partition. - * - * @return array An array with two elements. The first element contains the collection - * of elements where the predicate returned TRUE, the second element - * contains the collection of elements where the predicate returned FALSE. - */ - function partition(Closure $p); - - /** - * Gets the index/key of a given element. The comparison of two elements is strict, - * that means not only the value but also the type must match. - * For objects this means reference equality. - * - * @param mixed $element The element to search for. - * - * @return int|string|bool The key/index of the element or FALSE if the element was not found. - */ - function indexOf($element); - - /** - * Extracts a slice of $length elements starting at position $offset from the Collection. - * - * If $length is null it returns all elements from $offset to the end of the Collection. - * Keys have to be preserved by this method. Calling this method will only return the - * selected slice and NOT change the elements contained in the collection slice is called on. - * - * @param int $offset The offset to start from. - * @param int|null $length The maximum number of elements to return, or null for no limit. - * - * @return array - */ - function slice($offset, $length = null); -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php deleted file mode 100644 index 42929bdca..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php +++ /dev/null @@ -1,245 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -use Doctrine\Common\Collections\Expr\Expression; -use Doctrine\Common\Collections\Expr\CompositeExpression; - -/** - * Criteria for filtering Selectable collections. - * - * @author Benjamin Eberlei - * @since 2.3 - */ -class Criteria -{ - /** - * @var string - */ - const ASC = 'ASC'; - - /** - * @var string - */ - const DESC = 'DESC'; - - /** - * @var \Doctrine\Common\Collections\ExpressionBuilder|null - */ - private static $expressionBuilder; - - /** - * @var \Doctrine\Common\Collections\Expr\Expression|null - */ - private $expression; - - /** - * @var array|null - */ - private $orderings; - - /** - * @var int|null - */ - private $firstResult; - - /** - * @var int|null - */ - private $maxResults; - - /** - * Creates an instance of the class. - * - * @return Criteria - */ - public static function create() - { - return new static(); - } - - /** - * Returns the expression builder. - * - * @return \Doctrine\Common\Collections\ExpressionBuilder - */ - public static function expr() - { - if (self::$expressionBuilder === null) { - self::$expressionBuilder = new ExpressionBuilder(); - } - return self::$expressionBuilder; - } - - /** - * Construct a new Criteria. - * - * @param Expression $expression - * @param array|null $orderings - * @param int|null $firstResult - * @param int|null $maxResults - */ - public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) - { - $this->expression = $expression; - $this->orderings = $orderings; - $this->firstResult = $firstResult; - $this->maxResults = $maxResults; - } - - /** - * Sets the where expression to evaluate when this Criteria is searched for. - * - * @param Expression $expression - * - * @return Criteria - */ - public function where(Expression $expression) - { - $this->expression = $expression; - return $this; - } - - /** - * Appends the where expression to evaluate when this Criteria is searched for - * using an AND with previous expression. - * - * @param Expression $expression - * - * @return Criteria - */ - public function andWhere(Expression $expression) - { - if ($this->expression === null) { - return $this->where($expression); - } - - $this->expression = new CompositeExpression(CompositeExpression::TYPE_AND, array( - $this->expression, $expression - )); - - return $this; - } - - /** - * Appends the where expression to evaluate when this Criteria is searched for - * using an OR with previous expression. - * - * @param Expression $expression - * - * @return Criteria - */ - public function orWhere(Expression $expression) - { - if ($this->expression === null) { - return $this->where($expression); - } - - $this->expression = new CompositeExpression(CompositeExpression::TYPE_OR, array( - $this->expression, $expression - )); - - return $this; - } - - /** - * Gets the expression attached to this Criteria. - * - * @return Expression|null - */ - public function getWhereExpression() - { - return $this->expression; - } - - /** - * Gets the current orderings of this Criteria. - * - * @return array - */ - public function getOrderings() - { - return $this->orderings; - } - - /** - * Sets the ordering of the result of this Criteria. - * - * Keys are field and values are the order, being either ASC or DESC. - * - * @see Criteria::ASC - * @see Criteria::DESC - * - * @param array $orderings - * - * @return Criteria - */ - public function orderBy(array $orderings) - { - $this->orderings = $orderings; - return $this; - } - - /** - * Gets the current first result option of this Criteria. - * - * @return int|null - */ - public function getFirstResult() - { - return $this->firstResult; - } - - /** - * Set the number of first result that this Criteria should return. - * - * @param int|null $firstResult The value to set. - * - * @return Criteria - */ - public function setFirstResult($firstResult) - { - $this->firstResult = $firstResult; - return $this; - } - - /** - * Gets maxResults. - * - * @return int|null - */ - public function getMaxResults() - { - return $this->maxResults; - } - - /** - * Sets maxResults. - * - * @param int|null $maxResults The value to set. - * - * @return Criteria - */ - public function setMaxResults($maxResults) - { - $this->maxResults = $maxResults; - return $this; - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php deleted file mode 100644 index 50994584c..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php +++ /dev/null @@ -1,227 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -/** - * Walks an expression graph and turns it into a PHP closure. - * - * This closure can be used with {@Collection#filter()} and is used internally - * by {@ArrayCollection#select()}. - * - * @author Benjamin Eberlei - * @since 2.3 - */ -class ClosureExpressionVisitor extends ExpressionVisitor -{ - /** - * Accesses the field of a given object. This field has to be public - * directly or indirectly (through an accessor get*, is*, or a magic - * method, __get, __call). - * - * @param object $object - * @param string $field - * - * @return mixed - */ - public static function getObjectFieldValue($object, $field) - { - if (is_array($object)) { - return $object[$field]; - } - - $accessors = array('get', 'is'); - - foreach ($accessors as $accessor) { - $accessor .= $field; - - if ( ! method_exists($object, $accessor)) { - continue; - } - - return $object->$accessor(); - } - - // __call should be triggered for get. - $accessor = $accessors[0] . $field; - - if (method_exists($object, '__call')) { - return $object->$accessor(); - } - - if ($object instanceof \ArrayAccess) { - return $object[$field]; - } - - return $object->$field; - } - - /** - * Helper for sorting arrays of objects based on multiple fields + orientations. - * - * @param string $name - * @param int $orientation - * @param \Closure $next - * - * @return \Closure - */ - public static function sortByField($name, $orientation = 1, \Closure $next = null) - { - if (!$next) { - $next = function() { - return 0; - }; - } - - return function ($a, $b) use ($name, $next, $orientation) { - $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); - $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); - - if ($aValue === $bValue) { - return $next($a, $b); - } - - return (($aValue > $bValue) ? 1 : -1) * $orientation; - }; - } - - /** - * {@inheritDoc} - */ - public function walkComparison(Comparison $comparison) - { - $field = $comparison->getField(); - $value = $comparison->getValue()->getValue(); // shortcut for walkValue() - - switch ($comparison->getOperator()) { - case Comparison::EQ: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; - }; - - case Comparison::NEQ: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; - }; - - case Comparison::LT: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; - }; - - case Comparison::LTE: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; - }; - - case Comparison::GT: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; - }; - - case Comparison::GTE: - return function ($object) use ($field, $value) { - return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; - }; - - case Comparison::IN: - return function ($object) use ($field, $value) { - return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); - }; - - case Comparison::NIN: - return function ($object) use ($field, $value) { - return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); - }; - - case Comparison::CONTAINS: - return function ($object) use ($field, $value) { - return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); - }; - - default: - throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); - } - } - - /** - * {@inheritDoc} - */ - public function walkValue(Value $value) - { - return $value->getValue(); - } - - /** - * {@inheritDoc} - */ - public function walkCompositeExpression(CompositeExpression $expr) - { - $expressionList = array(); - - foreach ($expr->getExpressionList() as $child) { - $expressionList[] = $this->dispatch($child); - } - - switch($expr->getType()) { - case CompositeExpression::TYPE_AND: - return $this->andExpressions($expressionList); - - case CompositeExpression::TYPE_OR: - return $this->orExpressions($expressionList); - - default: - throw new \RuntimeException("Unknown composite " . $expr->getType()); - } - } - - /** - * @param array $expressions - * - * @return callable - */ - private function andExpressions($expressions) - { - return function ($object) use ($expressions) { - foreach ($expressions as $expression) { - if ( ! $expression($object)) { - return false; - } - } - return true; - }; - } - - /** - * @param array $expressions - * - * @return callable - */ - private function orExpressions($expressions) - { - return function ($object) use ($expressions) { - foreach ($expressions as $expression) { - if ($expression($object)) { - return true; - } - } - return false; - }; - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php deleted file mode 100644 index d54ecf25c..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php +++ /dev/null @@ -1,103 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -/** - * Comparison of a field with a value by the given operator. - * - * @author Benjamin Eberlei - * @since 2.3 - */ -class Comparison implements Expression -{ - const EQ = '='; - const NEQ = '<>'; - const LT = '<'; - const LTE = '<='; - const GT = '>'; - const GTE = '>='; - const IS = '='; // no difference with EQ - const IN = 'IN'; - const NIN = 'NIN'; - const CONTAINS = 'CONTAINS'; - - /** - * @var string - */ - private $field; - - /** - * @var string - */ - private $op; - - /** - * @var Value - */ - private $value; - - /** - * @param string $field - * @param string $operator - * @param mixed $value - */ - public function __construct($field, $operator, $value) - { - if ( ! ($value instanceof Value)) { - $value = new Value($value); - } - - $this->field = $field; - $this->op = $operator; - $this->value = $value; - } - - /** - * @return string - */ - public function getField() - { - return $this->field; - } - - /** - * @return Value - */ - public function getValue() - { - return $this->value; - } - - /** - * @return string - */ - public function getOperator() - { - return $this->op; - } - - /** - * {@inheritDoc} - */ - public function visit(ExpressionVisitor $visitor) - { - return $visitor->walkComparison($this); - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php deleted file mode 100644 index 3613c0274..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php +++ /dev/null @@ -1,90 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -/** - * Expression of Expressions combined by AND or OR operation. - * - * @author Benjamin Eberlei - * @since 2.3 - */ -class CompositeExpression implements Expression -{ - const TYPE_AND = 'AND'; - const TYPE_OR = 'OR'; - - /** - * @var string - */ - private $type; - - /** - * @var Expression[] - */ - private $expressions = array(); - - /** - * @param string $type - * @param array $expressions - * - * @throws \RuntimeException - */ - public function __construct($type, array $expressions) - { - $this->type = $type; - - foreach ($expressions as $expr) { - if ($expr instanceof Value) { - throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); - } - if ( ! ($expr instanceof Expression)) { - throw new \RuntimeException("No expression given to CompositeExpression."); - } - - $this->expressions[] = $expr; - } - } - - /** - * Returns the list of expressions nested in this composite. - * - * @return Expression[] - */ - public function getExpressionList() - { - return $this->expressions; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - - /** - * {@inheritDoc} - */ - public function visit(ExpressionVisitor $visitor) - { - return $visitor->walkCompositeExpression($this); - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php deleted file mode 100644 index 68db767c0..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php +++ /dev/null @@ -1,35 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -/** - * Expression for the {@link Selectable} interface. - * - * @author Benjamin Eberlei - */ -interface Expression -{ - /** - * @param ExpressionVisitor $visitor - * - * @return mixed - */ - public function visit(ExpressionVisitor $visitor); -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php deleted file mode 100644 index 080afdc6d..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php +++ /dev/null @@ -1,82 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -/** - * An Expression visitor walks a graph of expressions and turns them into a - * query for the underlying implementation. - * - * @author Benjamin Eberlei - */ -abstract class ExpressionVisitor -{ - /** - * Converts a comparison expression into the target query language output. - * - * @param Comparison $comparison - * - * @return mixed - */ - abstract public function walkComparison(Comparison $comparison); - - /** - * Converts a value expression into the target query language part. - * - * @param Value $value - * - * @return mixed - */ - abstract public function walkValue(Value $value); - - /** - * Converts a composite expression into the target query language output. - * - * @param CompositeExpression $expr - * - * @return mixed - */ - abstract public function walkCompositeExpression(CompositeExpression $expr); - - /** - * Dispatches walking an expression to the appropriate handler. - * - * @param Expression $expr - * - * @return mixed - * - * @throws \RuntimeException - */ - public function dispatch(Expression $expr) - { - switch (true) { - case ($expr instanceof Comparison): - return $this->walkComparison($expr); - - case ($expr instanceof Value): - return $this->walkValue($expr); - - case ($expr instanceof CompositeExpression): - return $this->walkCompositeExpression($expr); - - default: - throw new \RuntimeException("Unknown Expression " . get_class($expr)); - } - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php deleted file mode 100644 index 7f6e83143..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php +++ /dev/null @@ -1,52 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections\Expr; - -class Value implements Expression -{ - /** - * @var mixed - */ - private $value; - - /** - * @param mixed $value - */ - public function __construct($value) - { - $this->value = $value; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - /** - * {@inheritDoc} - */ - public function visit(ExpressionVisitor $visitor) - { - return $visitor->walkValue($this); - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php deleted file mode 100644 index 6539e3c75..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php +++ /dev/null @@ -1,166 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -use Doctrine\Common\Collections\Expr\Comparison; -use Doctrine\Common\Collections\Expr\CompositeExpression; -use Doctrine\Common\Collections\Expr\Value; - -/** - * Builder for Expressions in the {@link Selectable} interface. - * - * Important Notice for interoperable code: You have to use scalar - * values only for comparisons, otherwise the behavior of the comparision - * may be different between implementations (Array vs ORM vs ODM). - * - * @author Benjamin Eberlei - * @since 2.3 - */ -class ExpressionBuilder -{ - /** - * @param mixed $x - * - * @return CompositeExpression - */ - public function andX($x = null) - { - return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); - } - - /** - * @param mixed $x - * - * @return CompositeExpression - */ - public function orX($x = null) - { - return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function eq($field, $value) - { - return new Comparison($field, Comparison::EQ, new Value($value)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function gt($field, $value) - { - return new Comparison($field, Comparison::GT, new Value($value)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function lt($field, $value) - { - return new Comparison($field, Comparison::LT, new Value($value)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function gte($field, $value) - { - return new Comparison($field, Comparison::GTE, new Value($value)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function lte($field, $value) - { - return new Comparison($field, Comparison::LTE, new Value($value)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function neq($field, $value) - { - return new Comparison($field, Comparison::NEQ, new Value($value)); - } - - /** - * @param string $field - * - * @return Comparison - */ - public function isNull($field) - { - return new Comparison($field, Comparison::EQ, new Value(null)); - } - - /** - * @param string $field - * @param mixed $values - * - * @return Comparison - */ - public function in($field, array $values) - { - return new Comparison($field, Comparison::IN, new Value($values)); - } - - /** - * @param string $field - * @param mixed $values - * - * @return Comparison - */ - public function notIn($field, array $values) - { - return new Comparison($field, Comparison::NIN, new Value($values)); - } - - /** - * @param string $field - * @param mixed $value - * - * @return Comparison - */ - public function contains($field, $value) - { - return new Comparison($field, Comparison::CONTAINS, new Value($value)); - } -} diff --git a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php b/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php deleted file mode 100644 index 401d46e4d..000000000 --- a/core/lib/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php +++ /dev/null @@ -1,48 +0,0 @@ -. - */ - -namespace Doctrine\Common\Collections; - -/** - * Interface for collections that allow efficient filtering with an expression API. - * - * Goal of this interface is a backend independent method to fetch elements - * from a collections. {@link Expression} is crafted in a way that you can - * implement queries from both in-memory and database-backed collections. - * - * For database backed collections this allows very efficient access by - * utilizing the query APIs, for example SQL in the ORM. Applications using - * this API can implement efficient database access without having to ask the - * EntityManager or Repositories. - * - * @author Benjamin Eberlei - * @since 2.3 - */ -interface Selectable -{ - /** - * Selects all elements from a selectable that match the expression and - * returns a new collection containing these elements. - * - * @param Criteria $criteria - * - * @return Collection - */ - function matching(Criteria $criteria); -} diff --git a/core/lib/doctrine/collections/phpunit.xml.dist b/core/lib/doctrine/collections/phpunit.xml.dist deleted file mode 100644 index 36968e99c..000000000 --- a/core/lib/doctrine/collections/phpunit.xml.dist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - ./tests/Doctrine/ - - - - - - ./lib/Doctrine/ - - - - - - performance - - - diff --git a/core/lib/guzzle/guzzle/.gitignore b/core/lib/guzzle/guzzle/.gitignore deleted file mode 100644 index 893035d5b..000000000 --- a/core/lib/guzzle/guzzle/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Ingore common cruft -.DS_STORE -coverage -.idea - -# Ignore binary files -guzzle.phar -guzzle-min.phar - -# Ignore potentially sensitive phpunit file -phpunit.xml - -# Ignore composer generated files -composer.phar -composer.lock -composer-test.lock -vendor/ - -# Ignore build files -build/ -phing/build.properties - -# Ignore subsplit working directory -.subsplit - -docs/_build -docs/*.pyc diff --git a/core/lib/guzzle/guzzle/.travis.yml b/core/lib/guzzle/guzzle/.travis.yml deleted file mode 100644 index 56d283848..000000000 --- a/core/lib/guzzle/guzzle/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 - - 5.6 - - hhvm - -before_script: - - curl --version - - pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini - - echo 'Installing pecl_http' - - wget --quiet http://pecl.php.net/get/pecl_http-1.7.6.tgz - - tar -xzf pecl_http-1.7.6.tgz - - sh -c "cd pecl_http-1.7.6 && phpize && ./configure && make && sudo make install" > /dev/null - - echo "extension=http.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` - - pecl install uri_template-beta - - phpenv rehash - - composer install --dev - - echo 'Ensuring the correct version of node is running' - - ~/.nvm/nvm.sh install v0.6.14 - -script: vendor/bin/phpunit - -matrix: - allow_failures: - - php: 5.6 - - php: hhvm - fast_finish: true diff --git a/core/lib/guzzle/guzzle/CHANGELOG.md b/core/lib/guzzle/guzzle/CHANGELOG.md deleted file mode 100644 index a1ba31fd3..000000000 --- a/core/lib/guzzle/guzzle/CHANGELOG.md +++ /dev/null @@ -1,788 +0,0 @@ -CHANGELOG -========= - -3.9.1 (2014-05-07) ------------------- - -* Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. -* Added a fix to the stream checksum function so that when the first read - returns a falsey value, it still continues to consume the stream until EOF. - -3.9.0 (2014-04-23) ------------------- - -* `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value - with no trailing "=". See dc1d824277. -* No longer performing an MD5 check on the cacert each time the phar is used, - but rather copying the cacert to the temp directory. -* `"0"` can now be added as a URL path -* Deleting cookies that are set to empty -* If-Modified-Since is no longer unnecessarily added to the CachePlugin -* Cookie path matching now follows RFC 6265 s5.1.4 -* Updated service descriptions are now added to a service client's composite - factory. -* MockPlugin now throws an exception if the queue is empty. -* Properly parsing URLs that start with "http" but are not absolute -* Added the ability to configure the curl_multi_select timeout setting -* OAuth parameters are now sorted using lexicographical byte value ordering -* Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin - -3.8.1 (2014-01-28) ------------------- - -* Bug: Always using GET requests when redirecting from a 303 response -* Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in - `Guzzle\Http\ClientInterface::setSslVerification()` -* Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL -* Bug: The body of a request can now be set to `"0"` -* Sending PHP stream requests no longer forces `HTTP/1.0` -* Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of - each sub-exception -* Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than - clobbering everything). -* Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) -* Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. - For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. -* Now properly escaping the regular expression delimiter when matching Cookie domains. -* Network access is now disabled when loading XML documents - -3.8.0 (2013-12-05) ------------------- - -* Added the ability to define a POST name for a file -* JSON response parsing now properly walks additionalProperties -* cURL error code 18 is now retried automatically in the BackoffPlugin -* Fixed a cURL error when URLs contain fragments -* Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were - CurlExceptions -* CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) -* Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` -* Fixed a bug that was encountered when parsing empty header parameters -* UriTemplate now has a `setRegex()` method to match the docs -* The `debug` request parameter now checks if it is truthy rather than if it exists -* Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin -* Added the ability to combine URLs using strict RFC 3986 compliance -* Command objects can now return the validation errors encountered by the command -* Various fixes to cache revalidation (#437 and 29797e5) -* Various fixes to the AsyncPlugin -* Cleaned up build scripts - -3.7.4 (2013-10-02) ------------------- - -* Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) -* Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp - (see https://github.com/aws/aws-sdk-php/issues/147) -* Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots -* Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) -* Updated the bundled cacert.pem (#419) -* OauthPlugin now supports adding authentication to headers or query string (#425) - -3.7.3 (2013-09-08) ------------------- - -* Added the ability to get the exception associated with a request/command when using `MultiTransferException` and - `CommandTransferException`. -* Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description -* Schemas are only injected into response models when explicitly configured. -* No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of - an EntityBody. -* Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. -* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. -* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() -* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin -* Bug fix: Visiting XML attributes first before visting XML children when serializing requests -* Bug fix: Properly parsing headers that contain commas contained in quotes -* Bug fix: mimetype guessing based on a filename is now case-insensitive - -3.7.2 (2013-08-02) ------------------- - -* Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander - See https://github.com/guzzle/guzzle/issues/371 -* Bug fix: Cookie domains are now matched correctly according to RFC 6265 - See https://github.com/guzzle/guzzle/issues/377 -* Bug fix: GET parameters are now used when calculating an OAuth signature -* Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted -* `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched -* `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. - See https://github.com/guzzle/guzzle/issues/379 -* Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See - https://github.com/guzzle/guzzle/pull/380 -* cURL multi cleanup and optimizations - -3.7.1 (2013-07-05) ------------------- - -* Bug fix: Setting default options on a client now works -* Bug fix: Setting options on HEAD requests now works. See #352 -* Bug fix: Moving stream factory before send event to before building the stream. See #353 -* Bug fix: Cookies no longer match on IP addresses per RFC 6265 -* Bug fix: Correctly parsing header parameters that are in `<>` and quotes -* Added `cert` and `ssl_key` as request options -* `Host` header can now diverge from the host part of a URL if the header is set manually -* `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter -* OAuth parameters are only added via the plugin if they aren't already set -* Exceptions are now thrown when a URL cannot be parsed -* Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails -* Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin - -3.7.0 (2013-06-10) ------------------- - -* See UPGRADING.md for more information on how to upgrade. -* Requests now support the ability to specify an array of $options when creating a request to more easily modify a - request. You can pass a 'request.options' configuration setting to a client to apply default request options to - every request created by a client (e.g. default query string variables, headers, curl options, etc). -* Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. - See `Guzzle\Http\StaticClient::mount`. -* Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests - created by a command (e.g. custom headers, query string variables, timeout settings, etc). -* Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the - headers of a response -* Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key - (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) -* ServiceBuilders now support storing and retrieving arbitrary data -* CachePlugin can now purge all resources for a given URI -* CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource -* CachePlugin now uses the Vary header to determine if a resource is a cache hit -* `Guzzle\Http\Message\Response` now implements `\Serializable` -* Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters -* `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable -* Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` -* Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size -* `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message -* Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older - Symfony users can still use the old version of Monolog. -* Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. - Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. -* Several performance improvements to `Guzzle\Common\Collection` -* Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: - createRequest, head, delete, put, patch, post, options, prepareRequest -* Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` -* Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` -* Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to - `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a - resource, string, or EntityBody into the $options parameter to specify the download location of the response. -* Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a - default `array()` -* Added `Guzzle\Stream\StreamInterface::isRepeatable` -* Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use - $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or - $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. -* Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. -* Removed `Guzzle\Http\ClientInterface::expandTemplate()` -* Removed `Guzzle\Http\ClientInterface::setRequestFactory()` -* Removed `Guzzle\Http\ClientInterface::getCurlMulti()` -* Removed `Guzzle\Http\Message\RequestInterface::canCache` -* Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` -* Removed `Guzzle\Http\Message\RequestInterface::isRedirect` -* Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. -* You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting - `Guzzle\Common\Version::$emitWarnings` to true. -* Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use - `$request->getResponseBody()->isRepeatable()` instead. -* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use - `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -* Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use - `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -* Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. -* Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. -* Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated -* Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. - These will work through Guzzle 4.0 -* Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. -* Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. -* Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. -* Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. -* Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. -* Marked `Guzzle\Common\Collection::inject()` as deprecated. -* Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` -* CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a - CacheStorageInterface. These two objects and interface will be removed in a future version. -* Always setting X-cache headers on cached responses -* Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin -* `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface - $request, Response $response);` -* `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` -* `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` -* Added `CacheStorageInterface::purge($url)` -* `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin - $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, - CanCacheStrategyInterface $canCache = null)` -* Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` - -3.6.0 (2013-05-29) ------------------- - -* ServiceDescription now implements ToArrayInterface -* Added command.hidden_params to blacklist certain headers from being treated as additionalParameters -* Guzzle can now correctly parse incomplete URLs -* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. -* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution -* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). -* Specific header implementations can be created for complex headers. When a message creates a header, it uses a - HeaderFactory which can map specific headers to specific header classes. There is now a Link header and - CacheControl header implementation. -* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate -* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() -* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in - Guzzle\Http\Curl\RequestMediator -* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. -* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface -* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() -* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() -* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). -* All response header helper functions return a string rather than mixing Header objects and strings inconsistently -* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle - directly via interfaces -* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist - but are a no-op until removed. -* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a - `Guzzle\Service\Command\ArrayCommandInterface`. -* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response - on a request while the request is still being transferred -* The ability to case-insensitively search for header values -* Guzzle\Http\Message\Header::hasExactHeader -* Guzzle\Http\Message\Header::raw. Use getAll() -* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object - instead. -* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess -* Added the ability to cast Model objects to a string to view debug information. - -3.5.0 (2013-05-13) ------------------- - -* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times -* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove - itself from the EventDispatcher) -* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values -* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too -* Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a - non-existent key -* Bug: All __call() method arguments are now required (helps with mocking frameworks) -* Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference - to help with refcount based garbage collection of resources created by sending a request -* Deprecating ZF1 cache and log adapters. These will be removed in the next major version. -* Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the - HistoryPlugin for a history. -* Added a `responseBody` alias for the `response_body` location -* Refactored internals to no longer rely on Response::getRequest() -* HistoryPlugin can now be cast to a string -* HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests - and responses that are sent over the wire -* Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects - -3.4.3 (2013-04-30) ------------------- - -* Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response -* Added a check to re-extract the temp cacert bundle from the phar before sending each request - -3.4.2 (2013-04-29) ------------------- - -* Bug fix: Stream objects now work correctly with "a" and "a+" modes -* Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present -* Bug fix: AsyncPlugin no longer forces HEAD requests -* Bug fix: DateTime timezones are now properly handled when using the service description schema formatter -* Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails -* Setting a response on a request will write to the custom request body from the response body if one is specified -* LogPlugin now writes to php://output when STDERR is undefined -* Added the ability to set multiple POST files for the same key in a single call -* application/x-www-form-urlencoded POSTs now use the utf-8 charset by default -* Added the ability to queue CurlExceptions to the MockPlugin -* Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) -* Configuration loading now allows remote files - -3.4.1 (2013-04-16) ------------------- - -* Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti - handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. -* Exceptions are now properly grouped when sending requests in parallel -* Redirects are now properly aggregated when a multi transaction fails -* Redirects now set the response on the original object even in the event of a failure -* Bug fix: Model names are now properly set even when using $refs -* Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax -* Added support for oauth_callback in OAuth signatures -* Added support for oauth_verifier in OAuth signatures -* Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection - -3.4.0 (2013-04-11) ------------------- - -* Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 -* Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 -* Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 -* Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. -* Bug fix: Added `number` type to service descriptions. -* Bug fix: empty parameters are removed from an OAuth signature -* Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header -* Bug fix: Fixed "array to string" error when validating a union of types in a service description -* Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream -* Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. -* Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. -* The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. -* Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if - the Content-Type can be determined based on the entity body or the path of the request. -* Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. -* Added support for a PSR-3 LogAdapter. -* Added a `command.after_prepare` event -* Added `oauth_callback` parameter to the OauthPlugin -* Added the ability to create a custom stream class when using a stream factory -* Added a CachingEntityBody decorator -* Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. -* The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. -* You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies -* POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This - means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use - POST fields or files (the latter is only used when emulating a form POST in the browser). -* Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest - -3.3.1 (2013-03-10) ------------------- - -* Added the ability to create PHP streaming responses from HTTP requests -* Bug fix: Running any filters when parsing response headers with service descriptions -* Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing -* Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across - response location visitors. -* Bug fix: Removed the possibility of creating configuration files with circular dependencies -* RequestFactory::create() now uses the key of a POST file when setting the POST file name -* Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set - -3.3.0 (2013-03-03) ------------------- - -* A large number of performance optimizations have been made -* Bug fix: Added 'wb' as a valid write mode for streams -* Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned -* Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` -* BC: Removed `Guzzle\Http\Utils` class -* BC: Setting a service description on a client will no longer modify the client's command factories. -* BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using - the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' -* BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to - lowercase -* Operation parameter objects are now lazy loaded internally -* Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses -* Added support for instantiating responseType=class responseClass classes. Classes must implement - `Guzzle\Service\Command\ResponseClassInterface` -* Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These - additional properties also support locations and can be used to parse JSON responses where the outermost part of the - JSON is an array -* Added support for nested renaming of JSON models (rename sentAs to name) -* CachePlugin - * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error - * Debug headers can now added to cached response in the CachePlugin - -3.2.0 (2013-02-14) ------------------- - -* CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. -* URLs with no path no longer contain a "/" by default -* Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. -* BadResponseException no longer includes the full request and response message -* Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface -* Adding getResponseBody() to Guzzle\Http\Message\RequestInterface -* Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription -* Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list -* xmlEncoding can now be customized for the XML declaration of a XML service description operation -* Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value - aggregation and no longer uses callbacks -* The URL encoding implementation of Guzzle\Http\QueryString can now be customized -* Bug fix: Filters were not always invoked for array service description parameters -* Bug fix: Redirects now use a target response body rather than a temporary response body -* Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded -* Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives - -3.1.2 (2013-01-27) ------------------- - -* Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the - response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. -* Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent -* CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) -* Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() -* Setting default headers on a client after setting the user-agent will not erase the user-agent setting - -3.1.1 (2013-01-20) ------------------- - -* Adding wildcard support to Guzzle\Common\Collection::getPath() -* Adding alias support to ServiceBuilder configs -* Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface - -3.1.0 (2013-01-12) ------------------- - -* BC: CurlException now extends from RequestException rather than BadResponseException -* BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() -* Added getData to ServiceDescriptionInterface -* Added context array to RequestInterface::setState() -* Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http -* Bug: Adding required content-type when JSON request visitor adds JSON to a command -* Bug: Fixing the serialization of a service description with custom data -* Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing - an array of successful and failed responses -* Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection -* Added Guzzle\Http\IoEmittingEntityBody -* Moved command filtration from validators to location visitors -* Added `extends` attributes to service description parameters -* Added getModels to ServiceDescriptionInterface - -3.0.7 (2012-12-19) ------------------- - -* Fixing phar detection when forcing a cacert to system if null or true -* Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` -* Cleaning up `Guzzle\Common\Collection::inject` method -* Adding a response_body location to service descriptions - -3.0.6 (2012-12-09) ------------------- - -* CurlMulti performance improvements -* Adding setErrorResponses() to Operation -* composer.json tweaks - -3.0.5 (2012-11-18) ------------------- - -* Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin -* Bug: Response body can now be a string containing "0" -* Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert -* Bug: QueryString::fromString now properly parses query string parameters that contain equal signs -* Added support for XML attributes in service description responses -* DefaultRequestSerializer now supports array URI parameter values for URI template expansion -* Added better mimetype guessing to requests and post files - -3.0.4 (2012-11-11) ------------------- - -* Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value -* Bug: Cookies can now be added that have a name, domain, or value set to "0" -* Bug: Using the system cacert bundle when using the Phar -* Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures -* Enhanced cookie jar de-duplication -* Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added -* Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies -* Added the ability to create any sort of hash for a stream rather than just an MD5 hash - -3.0.3 (2012-11-04) ------------------- - -* Implementing redirects in PHP rather than cURL -* Added PECL URI template extension and using as default parser if available -* Bug: Fixed Content-Length parsing of Response factory -* Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. -* Adding ToArrayInterface throughout library -* Fixing OauthPlugin to create unique nonce values per request - -3.0.2 (2012-10-25) ------------------- - -* Magic methods are enabled by default on clients -* Magic methods return the result of a command -* Service clients no longer require a base_url option in the factory -* Bug: Fixed an issue with URI templates where null template variables were being expanded - -3.0.1 (2012-10-22) ------------------- - -* Models can now be used like regular collection objects by calling filter, map, etc -* Models no longer require a Parameter structure or initial data in the constructor -* Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` - -3.0.0 (2012-10-15) ------------------- - -* Rewrote service description format to be based on Swagger - * Now based on JSON schema - * Added nested input structures and nested response models - * Support for JSON and XML input and output models - * Renamed `commands` to `operations` - * Removed dot class notation - * Removed custom types -* Broke the project into smaller top-level namespaces to be more component friendly -* Removed support for XML configs and descriptions. Use arrays or JSON files. -* Removed the Validation component and Inspector -* Moved all cookie code to Guzzle\Plugin\Cookie -* Magic methods on a Guzzle\Service\Client now return the command un-executed. -* Calling getResult() or getResponse() on a command will lazily execute the command if needed. -* Now shipping with cURL's CA certs and using it by default -* Added previousResponse() method to response objects -* No longer sending Accept and Accept-Encoding headers on every request -* Only sending an Expect header by default when a payload is greater than 1MB -* Added/moved client options: - * curl.blacklist to curl.option.blacklist - * Added ssl.certificate_authority -* Added a Guzzle\Iterator component -* Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin -* Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) -* Added a more robust caching plugin -* Added setBody to response objects -* Updating LogPlugin to use a more flexible MessageFormatter -* Added a completely revamped build process -* Cleaning up Collection class and removing default values from the get method -* Fixed ZF2 cache adapters - -2.8.8 (2012-10-15) ------------------- - -* Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did - -2.8.7 (2012-09-30) ------------------- - -* Bug: Fixed config file aliases for JSON includes -* Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests -* Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload -* Bug: Hardening request and response parsing to account for missing parts -* Bug: Fixed PEAR packaging -* Bug: Fixed Request::getInfo -* Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail -* Adding the ability for the namespace Iterator factory to look in multiple directories -* Added more getters/setters/removers from service descriptions -* Added the ability to remove POST fields from OAuth signatures -* OAuth plugin now supports 2-legged OAuth - -2.8.6 (2012-09-05) ------------------- - -* Added the ability to modify and build service descriptions -* Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command -* Added a `json` parameter location -* Now allowing dot notation for classes in the CacheAdapterFactory -* Using the union of two arrays rather than an array_merge when extending service builder services and service params -* Ensuring that a service is a string before doing strpos() checks on it when substituting services for references - in service builder config files. -* Services defined in two different config files that include one another will by default replace the previously - defined service, but you can now create services that extend themselves and merge their settings over the previous -* The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like - '_default' with a default JSON configuration file. - -2.8.5 (2012-08-29) ------------------- - -* Bug: Suppressed empty arrays from URI templates -* Bug: Added the missing $options argument from ServiceDescription::factory to enable caching -* Added support for HTTP responses that do not contain a reason phrase in the start-line -* AbstractCommand commands are now invokable -* Added a way to get the data used when signing an Oauth request before a request is sent - -2.8.4 (2012-08-15) ------------------- - -* Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin -* Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. -* Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream -* Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream -* Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) -* Added additional response status codes -* Removed SSL information from the default User-Agent header -* DELETE requests can now send an entity body -* Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries -* Added the ability of the MockPlugin to consume mocked request bodies -* LogPlugin now exposes request and response objects in the extras array - -2.8.3 (2012-07-30) ------------------- - -* Bug: Fixed a case where empty POST requests were sent as GET requests -* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body -* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new -* Added multiple inheritance to service description commands -* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` -* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything -* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles - -2.8.2 (2012-07-24) ------------------- - -* Bug: Query string values set to 0 are no longer dropped from the query string -* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` -* Bug: ``+`` is now treated as an encoded space when parsing query strings -* QueryString and Collection performance improvements -* Allowing dot notation for class paths in filters attribute of a service descriptions - -2.8.1 (2012-07-16) ------------------- - -* Loosening Event Dispatcher dependency -* POST redirects can now be customized using CURLOPT_POSTREDIR - -2.8.0 (2012-07-15) ------------------- - -* BC: Guzzle\Http\Query - * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) - * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() - * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) - * Changed the aggregation functions of QueryString to be static methods - * Can now use fromString() with querystrings that have a leading ? -* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters -* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body -* Cookies are no longer URL decoded by default -* Bug: URI template variables set to null are no longer expanded - -2.7.2 (2012-07-02) ------------------- - -* BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. -* BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() -* CachePlugin now allows for a custom request parameter function to check if a request can be cached -* Bug fix: CachePlugin now only caches GET and HEAD requests by default -* Bug fix: Using header glue when transferring headers over the wire -* Allowing deeply nested arrays for composite variables in URI templates -* Batch divisors can now return iterators or arrays - -2.7.1 (2012-06-26) ------------------- - -* Minor patch to update version number in UA string -* Updating build process - -2.7.0 (2012-06-25) ------------------- - -* BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. -* BC: Removed magic setX methods from commands -* BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method -* Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. -* Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) -* Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace -* Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin -* Added the ability to set POST fields and files in a service description -* Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method -* Adding a command.before_prepare event to clients -* Added BatchClosureTransfer and BatchClosureDivisor -* BatchTransferException now includes references to the batch divisor and transfer strategies -* Fixed some tests so that they pass more reliably -* Added Guzzle\Common\Log\ArrayLogAdapter - -2.6.6 (2012-06-10) ------------------- - -* BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin -* BC: Removing Guzzle\Service\Command\CommandSet -* Adding generic batching system (replaces the batch queue plugin and command set) -* Updating ZF cache and log adapters and now using ZF's composer repository -* Bug: Setting the name of each ApiParam when creating through an ApiCommand -* Adding result_type, result_doc, deprecated, and doc_url to service descriptions -* Bug: Changed the default cookie header casing back to 'Cookie' - -2.6.5 (2012-06-03) ------------------- - -* BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() -* BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from -* BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data -* BC: Renaming methods in the CookieJarInterface -* Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations -* Making the default glue for HTTP headers ';' instead of ',' -* Adding a removeValue to Guzzle\Http\Message\Header -* Adding getCookies() to request interface. -* Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() - -2.6.4 (2012-05-30) ------------------- - -* BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. -* BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand -* Bug: Fixing magic method command calls on clients -* Bug: Email constraint only validates strings -* Bug: Aggregate POST fields when POST files are present in curl handle -* Bug: Fixing default User-Agent header -* Bug: Only appending or prepending parameters in commands if they are specified -* Bug: Not requiring response reason phrases or status codes to match a predefined list of codes -* Allowing the use of dot notation for class namespaces when using instance_of constraint -* Added any_match validation constraint -* Added an AsyncPlugin -* Passing request object to the calculateWait method of the ExponentialBackoffPlugin -* Allowing the result of a command object to be changed -* Parsing location and type sub values when instantiating a service description rather than over and over at runtime - -2.6.3 (2012-05-23) ------------------- - -* [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. -* [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. -* You can now use an array of data when creating PUT request bodies in the request factory. -* Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. -* [Http] Adding support for Content-Type in multipart POST uploads per upload -* [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) -* Adding more POST data operations for easier manipulation of POST data. -* You can now set empty POST fields. -* The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. -* Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. -* CS updates - -2.6.2 (2012-05-19) ------------------- - -* [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. - -2.6.1 (2012-05-19) ------------------- - -* [BC] Removing 'path' support in service descriptions. Use 'uri'. -* [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. -* [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. -* [BC] Removing Guzzle\Common\XmlElement. -* All commands, both dynamic and concrete, have ApiCommand objects. -* Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. -* Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. -* Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. - -2.6.0 (2012-05-15) ------------------- - -* [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder -* [BC] Executing a Command returns the result of the command rather than the command -* [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. -* [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. -* [BC] Moving ResourceIterator* to Guzzle\Service\Resource -* [BC] Completely refactored ResourceIterators to iterate over a cloned command object -* [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate -* [BC] Guzzle\Guzzle is now deprecated -* Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject -* Adding Guzzle\Version class to give version information about Guzzle -* Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() -* Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data -* ServiceDescription and ServiceBuilder are now cacheable using similar configs -* Changing the format of XML and JSON service builder configs. Backwards compatible. -* Cleaned up Cookie parsing -* Trimming the default Guzzle User-Agent header -* Adding a setOnComplete() method to Commands that is called when a command completes -* Keeping track of requests that were mocked in the MockPlugin -* Fixed a caching bug in the CacheAdapterFactory -* Inspector objects can be injected into a Command object -* Refactoring a lot of code and tests to be case insensitive when dealing with headers -* Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL -* Adding the ability to set global option overrides to service builder configs -* Adding the ability to include other service builder config files from within XML and JSON files -* Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. - -2.5.0 (2012-05-08) ------------------- - -* Major performance improvements -* [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. -* [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. -* [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" -* Added the ability to passed parameters to all requests created by a client -* Added callback functionality to the ExponentialBackoffPlugin -* Using microtime in ExponentialBackoffPlugin to allow more granular backoff strategies. -* Rewinding request stream bodies when retrying requests -* Exception is thrown when JSON response body cannot be decoded -* Added configurable magic method calls to clients and commands. This is off by default. -* Fixed a defect that added a hash to every parsed URL part -* Fixed duplicate none generation for OauthPlugin. -* Emitting an event each time a client is generated by a ServiceBuilder -* Using an ApiParams object instead of a Collection for parameters of an ApiCommand -* cache.* request parameters should be renamed to params.cache.* -* Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. -* Added the ability to disable type validation of service descriptions -* ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/core/lib/guzzle/guzzle/LICENSE b/core/lib/guzzle/guzzle/LICENSE deleted file mode 100644 index d51aa6986..000000000 --- a/core/lib/guzzle/guzzle/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/guzzle/guzzle/README.md b/core/lib/guzzle/guzzle/README.md deleted file mode 100644 index e1fc0cc5a..000000000 --- a/core/lib/guzzle/guzzle/README.md +++ /dev/null @@ -1,36 +0,0 @@ -Guzzle, PHP HTTP client and webservice framework -================================================ - -[![Composer Downloads](https://poser.pugx.org/guzzle/guzzle/d/total.png)](https://packagist.org/packages/guzzle/guzzle) - [![Build Status](https://secure.travis-ci.org/guzzle/guzzle3.png?branch=master)](http://travis-ci.org/guzzle/guzzle3) - -Guzzle is a PHP HTTP client and framework for building RESTful web service clients. - -- Extremely powerful API provides all the power of cURL with a simple interface. -- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. -- Service description DSL allows you build awesome web service clients faster. -- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. - -Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) - -### Installing via Composer - -The recommended way to install Guzzle is through [Composer](http://getcomposer.org). - -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Add Guzzle as a dependency -php composer.phar require guzzle/guzzle:~3.9 -``` - -After installing, you need to require Composer's autoloader: - -```php -require 'vendor/autoload.php'; -``` - -# This is an older version - -This repository is for Guzzle 3.x. Guzzle 4.0, the new version of Guzzle has been released and is available at https://github.com/guzzle/guzzle. diff --git a/core/lib/guzzle/guzzle/UPGRADING.md b/core/lib/guzzle/guzzle/UPGRADING.md deleted file mode 100644 index f58bf1171..000000000 --- a/core/lib/guzzle/guzzle/UPGRADING.md +++ /dev/null @@ -1,537 +0,0 @@ -Guzzle Upgrade Guide -==================== - -3.6 to 3.7 ----------- - -### Deprecations - -- You can now enable E_USER_DEPRECATED warnings to see if you are using any deprecated methods.: - -```php -\Guzzle\Common\Version::$emitWarnings = true; -``` - -The following APIs and options have been marked as deprecated: - -- Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. -- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -- Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. -- Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. -- Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. -- Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated -- Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. -- Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. -- Marked `Guzzle\Common\Collection::inject()` as deprecated. -- Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use - `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` or - `$client->setDefaultOption('auth', array('user', 'pass', 'Basic|Digest|NTLM|Any'));` - -3.7 introduces `request.options` as a parameter for a client configuration and as an optional argument to all creational -request methods. When paired with a client's configuration settings, these options allow you to specify default settings -for various aspects of a request. Because these options make other previous configuration options redundant, several -configuration options and methods of a client and AbstractCommand have been deprecated. - -- Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use `$client->getDefaultOption('headers')`. -- Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use `$client->setDefaultOption('headers/{header_name}', 'value')`. -- Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use `$client->setDefaultOption('params/{param_name}', 'value')` -- Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 - - $command = $client->getCommand('foo', array( - 'command.headers' => array('Test' => '123'), - 'command.response_body' => '/path/to/file' - )); - - // Should be changed to: - - $command = $client->getCommand('foo', array( - 'command.request_options' => array( - 'headers' => array('Test' => '123'), - 'save_as' => '/path/to/file' - ) - )); - -### Interface changes - -Additions and changes (you will need to update any implementations or subclasses you may have created): - -- Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: - createRequest, head, delete, put, patch, post, options, prepareRequest -- Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` -- Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` -- Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to - `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a - resource, string, or EntityBody into the $options parameter to specify the download location of the response. -- Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a - default `array()` -- Added `Guzzle\Stream\StreamInterface::isRepeatable` -- Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. - -The following methods were removed from interfaces. All of these methods are still available in the concrete classes -that implement them, but you should update your code to use alternative methods: - -- Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use - `$client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or - `$client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))` or - `$client->setDefaultOption('headers/{header_name}', 'value')`. or - `$client->setDefaultOption('headers', array('header_name' => 'value'))`. -- Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use `$client->getConfig()->getPath('request.options/headers')`. -- Removed `Guzzle\Http\ClientInterface::expandTemplate()`. This is an implementation detail. -- Removed `Guzzle\Http\ClientInterface::setRequestFactory()`. This is an implementation detail. -- Removed `Guzzle\Http\ClientInterface::getCurlMulti()`. This is a very specific implementation detail. -- Removed `Guzzle\Http\Message\RequestInterface::canCache`. Use the CachePlugin. -- Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect`. Use the HistoryPlugin. -- Removed `Guzzle\Http\Message\RequestInterface::isRedirect`. Use the HistoryPlugin. - -### Cache plugin breaking changes - -- CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a - CacheStorageInterface. These two objects and interface will be removed in a future version. -- Always setting X-cache headers on cached responses -- Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin -- `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface - $request, Response $response);` -- `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` -- `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` -- Added `CacheStorageInterface::purge($url)` -- `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin - $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, - CanCacheStrategyInterface $canCache = null)` -- Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` - -3.5 to 3.6 ----------- - -* Mixed casing of headers are now forced to be a single consistent casing across all values for that header. -* Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution -* Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). - For example, setHeader() first removes the header using unset on a HeaderCollection and then calls addHeader(). - Keeping the Host header and URL host in sync is now handled by overriding the addHeader method in Request. -* Specific header implementations can be created for complex headers. When a message creates a header, it uses a - HeaderFactory which can map specific headers to specific header classes. There is now a Link header and - CacheControl header implementation. -* Moved getLinks() from Response to just be used on a Link header object. - -If you previously relied on Guzzle\Http\Message\Header::raw(), then you will need to update your code to use the -HeaderInterface (e.g. toArray(), getAll(), etc). - -### Interface changes - -* Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate -* Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() -* Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in - Guzzle\Http\Curl\RequestMediator -* Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. -* Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface -* Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() - -### Removed deprecated functions - -* Removed Guzzle\Parser\ParserRegister::get(). Use getParser() -* Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). - -### Deprecations - -* The ability to case-insensitively search for header values -* Guzzle\Http\Message\Header::hasExactHeader -* Guzzle\Http\Message\Header::raw. Use getAll() -* Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object - instead. - -### Other changes - -* All response header helper functions return a string rather than mixing Header objects and strings inconsistently -* Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle - directly via interfaces -* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist - but are a no-op until removed. -* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a - `Guzzle\Service\Command\ArrayCommandInterface`. -* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response - on a request while the request is still being transferred -* `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess - -3.3 to 3.4 ----------- - -Base URLs of a client now follow the rules of http://tools.ietf.org/html/rfc3986#section-5.2.2 when merging URLs. - -3.2 to 3.3 ----------- - -### Response::getEtag() quote stripping removed - -`Guzzle\Http\Message\Response::getEtag()` no longer strips quotes around the ETag response header - -### Removed `Guzzle\Http\Utils` - -The `Guzzle\Http\Utils` class was removed. This class was only used for testing. - -### Stream wrapper and type - -`Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase. - -### curl.emit_io became emit_io - -Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the -'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' - -3.1 to 3.2 ----------- - -### CurlMulti is no longer reused globally - -Before 3.2, the same CurlMulti object was reused globally for each client. This can cause issue where plugins added -to a single client can pollute requests dispatched from other clients. - -If you still wish to reuse the same CurlMulti object with each client, then you can add a listener to the -ServiceBuilder's `service_builder.create_client` event to inject a custom CurlMulti object into each client as it is -created. - -```php -$multi = new Guzzle\Http\Curl\CurlMulti(); -$builder = Guzzle\Service\Builder\ServiceBuilder::factory('/path/to/config.json'); -$builder->addListener('service_builder.create_client', function ($event) use ($multi) { - $event['client']->setCurlMulti($multi); -} -}); -``` - -### No default path - -URLs no longer have a default path value of '/' if no path was specified. - -Before: - -```php -$request = $client->get('http://www.foo.com'); -echo $request->getUrl(); -// >> http://www.foo.com/ -``` - -After: - -```php -$request = $client->get('http://www.foo.com'); -echo $request->getUrl(); -// >> http://www.foo.com -``` - -### Less verbose BadResponseException - -The exception message for `Guzzle\Http\Exception\BadResponseException` no longer contains the full HTTP request and -response information. You can, however, get access to the request and response object by calling `getRequest()` or -`getResponse()` on the exception object. - -### Query parameter aggregation - -Multi-valued query parameters are no longer aggregated using a callback function. `Guzzle\Http\Query` now has a -setAggregator() method that accepts a `Guzzle\Http\QueryAggregator\QueryAggregatorInterface` object. This object is -responsible for handling the aggregation of multi-valued query string variables into a flattened hash. - -2.8 to 3.x ----------- - -### Guzzle\Service\Inspector - -Change `\Guzzle\Service\Inspector::fromConfig` to `\Guzzle\Common\Collection::fromConfig` - -**Before** - -```php -use Guzzle\Service\Inspector; - -class YourClient extends \Guzzle\Service\Client -{ - public static function factory($config = array()) - { - $default = array(); - $required = array('base_url', 'username', 'api_key'); - $config = Inspector::fromConfig($config, $default, $required); - - $client = new self( - $config->get('base_url'), - $config->get('username'), - $config->get('api_key') - ); - $client->setConfig($config); - - $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); - - return $client; - } -``` - -**After** - -```php -use Guzzle\Common\Collection; - -class YourClient extends \Guzzle\Service\Client -{ - public static function factory($config = array()) - { - $default = array(); - $required = array('base_url', 'username', 'api_key'); - $config = Collection::fromConfig($config, $default, $required); - - $client = new self( - $config->get('base_url'), - $config->get('username'), - $config->get('api_key') - ); - $client->setConfig($config); - - $client->setDescription(ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . 'client.json')); - - return $client; - } -``` - -### Convert XML Service Descriptions to JSON - -**Before** - -```xml - - - - - - Get a list of groups - - - Uses a search query to get a list of groups - - - - Create a group - - - - - Delete a group by ID - - - - - - - Update a group - - - - - - -``` - -**After** - -```json -{ - "name": "Zendesk REST API v2", - "apiVersion": "2012-12-31", - "description":"Provides access to Zendesk views, groups, tickets, ticket fields, and users", - "operations": { - "list_groups": { - "httpMethod":"GET", - "uri": "groups.json", - "summary": "Get a list of groups" - }, - "search_groups":{ - "httpMethod":"GET", - "uri": "search.json?query=\"{query} type:group\"", - "summary": "Uses a search query to get a list of groups", - "parameters":{ - "query":{ - "location": "uri", - "description":"Zendesk Search Query", - "type": "string", - "required": true - } - } - }, - "create_group": { - "httpMethod":"POST", - "uri": "groups.json", - "summary": "Create a group", - "parameters":{ - "data": { - "type": "array", - "location": "body", - "description":"Group JSON", - "filters": "json_encode", - "required": true - }, - "Content-Type":{ - "type": "string", - "location":"header", - "static": "application/json" - } - } - }, - "delete_group": { - "httpMethod":"DELETE", - "uri": "groups/{id}.json", - "summary": "Delete a group", - "parameters":{ - "id":{ - "location": "uri", - "description":"Group to delete by ID", - "type": "integer", - "required": true - } - } - }, - "get_group": { - "httpMethod":"GET", - "uri": "groups/{id}.json", - "summary": "Get a ticket", - "parameters":{ - "id":{ - "location": "uri", - "description":"Group to get by ID", - "type": "integer", - "required": true - } - } - }, - "update_group": { - "httpMethod":"PUT", - "uri": "groups/{id}.json", - "summary": "Update a group", - "parameters":{ - "id": { - "location": "uri", - "description":"Group to update by ID", - "type": "integer", - "required": true - }, - "data": { - "type": "array", - "location": "body", - "description":"Group JSON", - "filters": "json_encode", - "required": true - }, - "Content-Type":{ - "type": "string", - "location":"header", - "static": "application/json" - } - } - } -} -``` - -### Guzzle\Service\Description\ServiceDescription - -Commands are now called Operations - -**Before** - -```php -use Guzzle\Service\Description\ServiceDescription; - -$sd = new ServiceDescription(); -$sd->getCommands(); // @returns ApiCommandInterface[] -$sd->hasCommand($name); -$sd->getCommand($name); // @returns ApiCommandInterface|null -$sd->addCommand($command); // @param ApiCommandInterface $command -``` - -**After** - -```php -use Guzzle\Service\Description\ServiceDescription; - -$sd = new ServiceDescription(); -$sd->getOperations(); // @returns OperationInterface[] -$sd->hasOperation($name); -$sd->getOperation($name); // @returns OperationInterface|null -$sd->addOperation($operation); // @param OperationInterface $operation -``` - -### Guzzle\Common\Inflection\Inflector - -Namespace is now `Guzzle\Inflection\Inflector` - -### Guzzle\Http\Plugin - -Namespace is now `Guzzle\Plugin`. Many other changes occur within this namespace and are detailed in their own sections below. - -### Guzzle\Http\Plugin\LogPlugin and Guzzle\Common\Log - -Now `Guzzle\Plugin\Log\LogPlugin` and `Guzzle\Log` respectively. - -**Before** - -```php -use Guzzle\Common\Log\ClosureLogAdapter; -use Guzzle\Http\Plugin\LogPlugin; - -/** @var \Guzzle\Http\Client */ -$client; - -// $verbosity is an integer indicating desired message verbosity level -$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $verbosity = LogPlugin::LOG_VERBOSE); -``` - -**After** - -```php -use Guzzle\Log\ClosureLogAdapter; -use Guzzle\Log\MessageFormatter; -use Guzzle\Plugin\Log\LogPlugin; - -/** @var \Guzzle\Http\Client */ -$client; - -// $format is a string indicating desired message format -- @see MessageFormatter -$client->addSubscriber(new LogPlugin(new ClosureLogAdapter(function($m) { echo $m; }, $format = MessageFormatter::DEBUG_FORMAT); -``` - -### Guzzle\Http\Plugin\CurlAuthPlugin - -Now `Guzzle\Plugin\CurlAuth\CurlAuthPlugin`. - -### Guzzle\Http\Plugin\ExponentialBackoffPlugin - -Now `Guzzle\Plugin\Backoff\BackoffPlugin`, and other changes. - -**Before** - -```php -use Guzzle\Http\Plugin\ExponentialBackoffPlugin; - -$backoffPlugin = new ExponentialBackoffPlugin($maxRetries, array_merge( - ExponentialBackoffPlugin::getDefaultFailureCodes(), array(429) - )); - -$client->addSubscriber($backoffPlugin); -``` - -**After** - -```php -use Guzzle\Plugin\Backoff\BackoffPlugin; -use Guzzle\Plugin\Backoff\HttpBackoffStrategy; - -// Use convenient factory method instead -- see implementation for ideas of what -// you can do with chaining backoff strategies -$backoffPlugin = BackoffPlugin::getExponentialBackoff($maxRetries, array_merge( - HttpBackoffStrategy::getDefaultFailureCodes(), array(429) - )); -$client->addSubscriber($backoffPlugin); -``` - -### Known Issues - -#### [BUG] Accept-Encoding header behavior changed unintentionally. - -(See #217) (Fixed in 09daeb8c666fb44499a0646d655a8ae36456575e) - -In version 2.8 setting the `Accept-Encoding` header would set the CURLOPT_ENCODING option, which permitted cURL to -properly handle gzip/deflate compressed responses from the server. In versions affected by this bug this does not happen. -See issue #217 for a workaround, or use a version containing the fix. diff --git a/core/lib/guzzle/guzzle/build.xml b/core/lib/guzzle/guzzle/build.xml deleted file mode 100644 index 2aa62ba9a..000000000 --- a/core/lib/guzzle/guzzle/build.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/lib/guzzle/guzzle/composer.json b/core/lib/guzzle/guzzle/composer.json deleted file mode 100644 index d6e9a9693..000000000 --- a/core/lib/guzzle/guzzle/composer.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "name": "guzzle/guzzle", - "type": "library", - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", - "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], - "homepage": "http://guzzlephp.org/", - "license": "MIT", - - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - }, - { - "name": "Guzzle Community", - "homepage": "https://github.com/guzzle/guzzle/contributors" - } - ], - - "replace": { - "guzzle/batch": "self.version", - "guzzle/cache": "self.version", - "guzzle/common": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version", - "guzzle/iterator": "self.version", - "guzzle/log": "self.version", - "guzzle/parser": "self.version", - "guzzle/plugin": "self.version", - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version", - "guzzle/service": "self.version", - "guzzle/stream": "self.version" - }, - - "require": { - "php": ">=5.3.3", - "ext-curl": "*", - "symfony/event-dispatcher": "~2.1" - }, - - "autoload": { - "psr-0": { - "Guzzle": "src/", - "Guzzle\\Tests": "tests/" - } - }, - - "require-dev": { - "doctrine/cache": "~1.3", - "symfony/class-loader": "~2.1", - "monolog/monolog": "~1.0", - "psr/log": "~1.0", - "zendframework/zend-cache": "2.*,<2.3", - "zendframework/zend-log": "2.*,<2.3", - "phpunit/phpunit": "3.7.*" - }, - - "extra": { - "branch-alias": { - "dev-master": "3.8-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/phar-stub.php b/core/lib/guzzle/guzzle/phar-stub.php deleted file mode 100644 index 504dfe078..000000000 --- a/core/lib/guzzle/guzzle/phar-stub.php +++ /dev/null @@ -1,28 +0,0 @@ -registerNamespaces(array( - 'Guzzle' => 'phar://guzzle.phar/src', - 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', - 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', - 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' -)); -$classLoader->register(); - -// Copy the cacert.pem file from the phar if it is not in the temp folder. -$from = 'phar://guzzle.phar/src/Guzzle/Http/Resources/cacert.pem'; -$certFile = sys_get_temp_dir() . '/guzzle-cacert.pem'; - -// Only copy when the file size is different -if (!file_exists($certFile) || filesize($certFile) != filesize($from)) { - if (!copy($from, $certFile)) { - throw new RuntimeException("Could not copy {$from} to {$certFile}: " - . var_export(error_get_last(), true)); - } -} - -__HALT_COMPILER(); diff --git a/core/lib/guzzle/guzzle/phing/build.properties.dist b/core/lib/guzzle/guzzle/phing/build.properties.dist deleted file mode 100644 index c60d3d9cf..000000000 --- a/core/lib/guzzle/guzzle/phing/build.properties.dist +++ /dev/null @@ -1,16 +0,0 @@ -# you may need to update this if you're working on a fork. -guzzle.remote=git@github.com:guzzle/guzzle.git - -# github credentials -- only used by GitHub API calls to create subtree repos -github.basicauth=username:password -# for the subtree split and testing -github.org=guzzle - -# your git path -cmd.git=git - -# your composer command -cmd.composer=composer - -# test server start -cmd.testserver=node diff --git a/core/lib/guzzle/guzzle/phing/imports/dependencies.xml b/core/lib/guzzle/guzzle/phing/imports/dependencies.xml deleted file mode 100644 index e40e037c2..000000000 --- a/core/lib/guzzle/guzzle/phing/imports/dependencies.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - using git at ${cmd.git} - - - - found git at ${cmd.git} - - - - - - - - - - diff --git a/core/lib/guzzle/guzzle/phing/imports/deploy.xml b/core/lib/guzzle/guzzle/phing/imports/deploy.xml deleted file mode 100644 index 109e5ec4f..000000000 --- a/core/lib/guzzle/guzzle/phing/imports/deploy.xml +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - - - - - On branch ${head} - - - - - - - - - - working directory clean - - - ${git.status} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ChangeLog Match: ${version.changelog} - Guzzle\Common\Version Match: ${version.version} - - - - releasing: phing -Dnew.version=3.0.x -Dhead=master release - -- - - - - - - - - - - - - - - - BEGINNING RELEASE FOR ${new.version} - - - - - - - - - - - - - - - - - - - - - - - - Tip: to create a new release, do: phing -Dnew.version=[TAG] -Dhead=[BRANCH] release - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php b/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php deleted file mode 100644 index 3b7040982..000000000 --- a/core/lib/guzzle/guzzle/phing/tasks/ComposerLintTask.php +++ /dev/null @@ -1,152 +0,0 @@ - - * @license http://claylo.mit-license.org/2012/ MIT License - */ - -require_once 'phing/Task.php'; - -class ComposerLintTask extends Task -{ - protected $dir = null; - protected $file = null; - protected $passthru = false; - protected $composer = null; - - /** - * The setter for the dir - * - * @param string $str Directory to crawl recursively for composer files - */ - public function setDir($str) - { - $this->dir = $str; - } - - /** - * The setter for the file - * - * @param string $str Individual file to validate - */ - public function setFile($str) - { - $this->file = $str; - } - - /** - * Whether to use PHP's passthru() function instead of exec() - * - * @param boolean $passthru If passthru shall be used - */ - public function setPassthru($passthru) - { - $this->passthru = (bool) $passthru; - } - - /** - * Composer to execute. If unset, will attempt composer.phar in project - * basedir, and if that fails, will attempt global composer - * installation. - * - * @param string $str Individual file to validate - */ - public function setComposer($str) - { - $this->file = $str; - } - - /** - * The init method: do init steps - */ - public function init() - { - // nothing needed here - } - - /** - * The main entry point - */ - public function main() - { - if ($this->composer === null) { - $this->findComposer(); - } - - $files = array(); - if (!empty($this->file) && file_exists($this->file)) { - $files[] = $this->file; - } - - if (!empty($this->dir)) { - $found = $this->findFiles(); - foreach ($found as $file) { - $files[] = $this->dir . DIRECTORY_SEPARATOR . $file; - } - } - - foreach ($files as $file) { - - $cmd = $this->composer . ' validate ' . $file; - $cmd = escapeshellcmd($cmd); - - if ($this->passthru) { - $retval = null; - passthru($cmd, $retval); - if ($retval == 1) { - throw new BuildException('invalid composer.json'); - } - } else { - $out = array(); - $retval = null; - exec($cmd, $out, $retval); - if ($retval == 1) { - $err = join("\n", $out); - throw new BuildException($err); - } else { - $this->log($out[0]); - } - } - - } - - } - - /** - * Find the composer.json files using Phing's directory scanner - * - * @return array - */ - protected function findFiles() - { - $ds = new DirectoryScanner(); - $ds->setBasedir($this->dir); - $ds->setIncludes(array('**/composer.json')); - $ds->scan(); - return $ds->getIncludedFiles(); - } - - /** - * Find composer installation - * - */ - protected function findComposer() - { - $basedir = $this->project->getBasedir(); - $php = $this->project->getProperty('php.interpreter'); - - if (file_exists($basedir . '/composer.phar')) { - $this->composer = "$php $basedir/composer.phar"; - } else { - $out = array(); - exec('which composer', $out); - if (empty($out)) { - throw new BuildException( - 'Could not determine composer location.' - ); - } - $this->composer = $out[0]; - } - } -} diff --git a/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php b/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php deleted file mode 100644 index f72a6b5d0..000000000 --- a/core/lib/guzzle/guzzle/phing/tasks/GuzzlePearPharPackageTask.php +++ /dev/null @@ -1,338 +0,0 @@ - - * @license http://claylo.mit-license.org/2012/ MIT License - */ - -require_once 'phing/Task.php'; -require_once 'PEAR/PackageFileManager2.php'; -require_once 'PEAR/PackageFileManager/File.php'; -require_once 'PEAR/Packager.php'; - -class GuzzlePearPharPackageTask extends Task -{ - private $version; - private $deploy = true; - private $makephar = true; - - private $subpackages = array(); - - public function setVersion($str) - { - $this->version = $str; - } - - public function getVersion() - { - return $this->version; - } - - public function setDeploy($deploy) - { - $this->deploy = (bool) $deploy; - } - - public function getDeploy() - { - return $this->deploy; - } - - public function setMakephar($makephar) - { - $this->makephar = (bool) $makephar; - } - - public function getMakephar() - { - return $this->makephar; - } - - private $basedir; - private $guzzleinfo; - private $changelog_release_date; - private $changelog_notes = '-'; - - public function main() - { - $this->basedir = $this->getProject()->getBasedir(); - - if (!is_dir((string) $this->basedir.'/.subsplit')) { - throw new BuildException('PEAR packaging requires .subsplit directory'); - } - - // main composer file - $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/composer.json'); - $this->guzzleinfo = json_decode($composer_file, true); - - // make sure we have a target - $pearwork = (string) $this->basedir . '/build/pearwork'; - if (!is_dir($pearwork)) { - mkdir($pearwork, 0777, true); - } - $pearlogs = (string) $this->basedir . '/build/artifacts/logs'; - if (!is_dir($pearlogs)) { - mkdir($pearlogs, 0777, true); - } - - $version = $this->getVersion(); - $this->grabChangelog(); - if ($version[0] == '2') { - $this->log('building single PEAR package'); - $this->buildSinglePackage(); - } else { - // $this->log("building PEAR subpackages"); - // $this->createSubPackages(); - // $this->log("building PEAR bundle package"); - $this->buildSinglePackage(); - } - - if ($this->getMakephar()) { - $this->log("building PHAR"); - $this->getProject()->executeTarget('package-phar'); - } - - if ($this->getDeploy()) { - $this->doDeployment(); - } - } - - public function doDeployment() - { - $basedir = (string) $this->basedir; - $this->log('beginning PEAR/PHAR deployment'); - - chdir($basedir . '/build/pearwork'); - if (!is_dir('./channel')) { - mkdir('./channel'); - } - - // Pull the PEAR channel down locally - passthru('aws s3 sync s3://pear.guzzlephp.org ./channel'); - - // add PEAR packages - foreach (scandir('./') as $file) { - if (substr($file, -4) == '.tgz') { - passthru('pirum add ./channel ' . $file); - } - } - - // if we have a new phar, add it - if ($this->getMakephar() && file_exists($basedir . '/build/artifacts/guzzle.phar')) { - rename($basedir . '/build/artifacts/guzzle.phar', './channel/guzzle.phar'); - } - - // Sync up with the S3 bucket - chdir($basedir . '/build/pearwork/channel'); - passthru('aws s3 sync . s3://pear.guzzlephp.org'); - } - - public function buildSinglePackage() - { - $v = $this->getVersion(); - $apiversion = $v[0] . '.0.0'; - - $opts = array( - 'packagedirectory' => (string) $this->basedir . '/.subsplit/src/', - 'filelistgenerator' => 'file', - 'ignore' => array('*composer.json'), - 'baseinstalldir' => '/', - 'packagefile' => 'package.xml' - //'outputdirectory' => (string) $this->basedir . '/build/pearwork/' - ); - $pfm = new PEAR_PackageFileManager2(); - $pfm->setOptions($opts); - $pfm->addRole('md', 'doc'); - $pfm->addRole('pem', 'php'); - $pfm->setPackage('Guzzle'); - $pfm->setSummary("Object-oriented PHP HTTP Client for PHP 5.3+"); - $pfm->setDescription($this->guzzleinfo['description']); - $pfm->setPackageType('php'); - $pfm->setChannel('guzzlephp.org/pear'); - $pfm->setAPIVersion($apiversion); - $pfm->setReleaseVersion($this->getVersion()); - $pfm->setAPIStability('stable'); - $pfm->setReleaseStability('stable'); - $pfm->setNotes($this->changelog_notes); - $pfm->setPackageType('php'); - $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); - $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); - $pfm->setDate($this->changelog_release_date); - $pfm->generateContents(); - - $phpdep = $this->guzzleinfo['require']['php']; - $phpdep = str_replace('>=', '', $phpdep); - $pfm->setPhpDep($phpdep); - $pfm->addExtensionDep('required', 'curl'); - $pfm->setPearinstallerDep('1.4.6'); - $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); - if (!empty($this->subpackages)) { - foreach ($this->subpackages as $package) { - $pkg = dirname($package); - $pkg = str_replace('/', '_', $pkg); - $pfm->addConflictingPackageDepWithChannel($pkg, 'guzzlephp.org/pear', false, $apiversion); - } - } - - ob_start(); - $startdir = getcwd(); - chdir((string) $this->basedir . '/build/pearwork'); - - echo "DEBUGGING GENERATED PACKAGE FILE\n"; - $result = $pfm->debugPackageFile(); - if ($result) { - $out = $pfm->writePackageFile(); - echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; - var_dump($out); - // load up package file and build package - $packager = new PEAR_Packager(); - echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; - $dest_package = $packager->package($opts['packagedirectory'].'package.xml'); - var_dump($dest_package); - } else { - echo "\n\n\nDEBUGGING RESULT:\n"; - var_dump($result); - } - echo "removing package.xml"; - unlink($opts['packagedirectory'].'package.xml'); - $log = ob_get_clean(); - file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package.log', $log); - chdir($startdir); - } - - public function createSubPackages() - { - $this->findComponents(); - - foreach ($this->subpackages as $package) { - $baseinstalldir = dirname($package); - $dir = (string) $this->basedir.'/.subsplit/src/' . $baseinstalldir; - $composer_file = file_get_contents((string) $this->basedir.'/.subsplit/src/'. $package); - $package_info = json_decode($composer_file, true); - $this->log('building ' . $package_info['target-dir'] . ' subpackage'); - $this->buildSubPackage($dir, $baseinstalldir, $package_info); - } - } - - public function buildSubPackage($dir, $baseinstalldir, $info) - { - $package = str_replace('/', '_', $baseinstalldir); - $opts = array( - 'packagedirectory' => $dir, - 'filelistgenerator' => 'file', - 'ignore' => array('*composer.json', '*package.xml'), - 'baseinstalldir' => '/' . $info['target-dir'], - 'packagefile' => 'package.xml' - ); - $pfm = new PEAR_PackageFileManager2(); - $pfm->setOptions($opts); - $pfm->setPackage($package); - $pfm->setSummary($info['description']); - $pfm->setDescription($info['description']); - $pfm->setPackageType('php'); - $pfm->setChannel('guzzlephp.org/pear'); - $pfm->setAPIVersion('3.0.0'); - $pfm->setReleaseVersion($this->getVersion()); - $pfm->setAPIStability('stable'); - $pfm->setReleaseStability('stable'); - $pfm->setNotes($this->changelog_notes); - $pfm->setPackageType('php'); - $pfm->setLicense('MIT', 'http://github.com/guzzle/guzzle/blob/master/LICENSE'); - $pfm->addMaintainer('lead', 'mtdowling', 'Michael Dowling', 'mtdowling@gmail.com', 'yes'); - $pfm->setDate($this->changelog_release_date); - $pfm->generateContents(); - - $phpdep = $this->guzzleinfo['require']['php']; - $phpdep = str_replace('>=', '', $phpdep); - $pfm->setPhpDep($phpdep); - $pfm->setPearinstallerDep('1.4.6'); - - foreach ($info['require'] as $type => $version) { - if ($type == 'php') { - continue; - } - if ($type == 'symfony/event-dispatcher') { - $pfm->addPackageDepWithChannel('required', 'EventDispatcher', 'pear.symfony.com', '2.1.0'); - } - if ($type == 'ext-curl') { - $pfm->addExtensionDep('required', 'curl'); - } - if (substr($type, 0, 6) == 'guzzle') { - $gdep = str_replace('/', ' ', $type); - $gdep = ucwords($gdep); - $gdep = str_replace(' ', '_', $gdep); - $pfm->addPackageDepWithChannel('required', $gdep, 'guzzlephp.org/pear', $this->getVersion()); - } - } - - // can't have main Guzzle package AND sub-packages - $pfm->addConflictingPackageDepWithChannel('Guzzle', 'guzzlephp.org/pear', false, $apiversion); - - ob_start(); - $startdir = getcwd(); - chdir((string) $this->basedir . '/build/pearwork'); - - echo "DEBUGGING GENERATED PACKAGE FILE\n"; - $result = $pfm->debugPackageFile(); - if ($result) { - $out = $pfm->writePackageFile(); - echo "\n\n\nWRITE PACKAGE FILE RESULT:\n"; - var_dump($out); - // load up package file and build package - $packager = new PEAR_Packager(); - echo "\n\n\nBUILDING PACKAGE FROM PACKAGE FILE:\n"; - $dest_package = $packager->package($opts['packagedirectory'].'/package.xml'); - var_dump($dest_package); - } else { - echo "\n\n\nDEBUGGING RESULT:\n"; - var_dump($result); - } - echo "removing package.xml"; - unlink($opts['packagedirectory'].'/package.xml'); - $log = ob_get_clean(); - file_put_contents((string) $this->basedir . '/build/artifacts/logs/pear_package_'.$package.'.log', $log); - chdir($startdir); - } - - public function findComponents() - { - $ds = new DirectoryScanner(); - $ds->setBasedir((string) $this->basedir.'/.subsplit/src'); - $ds->setIncludes(array('**/composer.json')); - $ds->scan(); - $files = $ds->getIncludedFiles(); - $this->subpackages = $files; - } - - public function grabChangelog() - { - $cl = file((string) $this->basedir.'/.subsplit/CHANGELOG.md'); - $notes = ''; - $in_version = false; - $release_date = null; - - foreach ($cl as $line) { - $line = trim($line); - if (preg_match('/^\* '.$this->getVersion().' \(([0-9\-]+)\)$/', $line, $matches)) { - $release_date = $matches[1]; - $in_version = true; - continue; - } - if ($in_version && empty($line) && empty($notes)) { - continue; - } - if ($in_version && ! empty($line)) { - $notes .= $line."\n"; - } - if ($in_version && empty($line) && !empty($notes)) { - $in_version = false; - } - } - $this->changelog_release_date = $release_date; - - if (! empty($notes)) { - $this->changelog_notes = $notes; - } - } -} diff --git a/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php b/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php deleted file mode 100644 index 5d56a5bd5..000000000 --- a/core/lib/guzzle/guzzle/phing/tasks/GuzzleSubSplitTask.php +++ /dev/null @@ -1,385 +0,0 @@ - - * @license http://claylo.mit-license.org/2012/ MIT License - */ - -require_once 'phing/tasks/ext/git/GitBaseTask.php'; - -// base - base of tree to split out -// subIndicatorFile - composer.json, package.xml? -class GuzzleSubSplitTask extends GitBaseTask -{ - /** - * What git repository to pull from and publish to - */ - protected $remote = null; - - /** - * Publish for comma-separated heads instead of all heads - */ - protected $heads = null; - - /** - * Publish for comma-separated tags instead of all tags - */ - protected $tags = null; - - /** - * Base of the tree RELATIVE TO .subsplit working dir - */ - protected $base = null; - - /** - * The presence of this file will indicate that the directory it resides - * in is at the top level of a split. - */ - protected $subIndicatorFile = 'composer.json'; - - /** - * Do everything except actually send the update. - */ - protected $dryRun = null; - - /** - * Do not sync any heads. - */ - protected $noHeads = false; - - /** - * Do not sync any tags. - */ - protected $noTags = false; - - /** - * The splits we found in the heads - */ - protected $splits; - - public function setRemote($str) - { - $this->remote = $str; - } - - public function getRemote() - { - return $this->remote; - } - - public function setHeads($str) - { - $this->heads = explode(',', $str); - } - - public function getHeads() - { - return $this->heads; - } - - public function setTags($str) - { - $this->tags = explode(',', $str); - } - - public function getTags() - { - return $this->tags; - } - - public function setBase($str) - { - $this->base = $str; - } - - public function getBase() - { - return $this->base; - } - - public function setSubIndicatorFile($str) - { - $this->subIndicatorFile = $str; - } - - public function getSubIndicatorFile() - { - return $this->subIndicatorFile; - } - - public function setDryRun($bool) - { - $this->dryRun = (bool) $bool; - } - - public function getDryRun() - { - return $this->dryRun; - } - - public function setNoHeads($bool) - { - $this->noHeads = (bool) $bool; - } - - public function getNoHeads() - { - return $this->noHeads; - } - - public function setNoTags($bool) - { - $this->noTags = (bool) $bool; - } - - public function getNoTags() - { - return $this->noTags; - } - - /** - * GitClient from VersionControl_Git - */ - protected $client = null; - - /** - * The main entry point - */ - public function main() - { - $repo = $this->getRepository(); - if (empty($repo)) { - throw new BuildException('"repository" is a required parameter'); - } - - $remote = $this->getRemote(); - if (empty($remote)) { - throw new BuildException('"remote" is a required parameter'); - } - - chdir($repo); - $this->client = $this->getGitClient(false, $repo); - - // initalized yet? - if (!is_dir('.subsplit')) { - $this->subsplitInit(); - } else { - // update - $this->subsplitUpdate(); - } - - // find all splits based on heads requested - $this->findSplits(); - - // check that GitHub has the repos - $this->verifyRepos(); - - // execute the subsplits - $this->publish(); - } - - public function publish() - { - $this->log('DRY RUN ONLY FOR NOW'); - $base = $this->getBase(); - $base = rtrim($base, '/') . '/'; - $org = $this->getOwningTarget()->getProject()->getProperty('github.org'); - - $splits = array(); - - $heads = $this->getHeads(); - foreach ($heads as $head) { - foreach ($this->splits[$head] as $component => $meta) { - $splits[] = $base . $component . ':git@github.com:'. $org.'/'.$meta['repo']; - } - - $cmd = 'git subsplit publish '; - $cmd .= escapeshellarg(implode(' ', $splits)); - - if ($this->getNoHeads()) { - $cmd .= ' --no-heads'; - } else { - $cmd .= ' --heads='.$head; - } - - if ($this->getNoTags()) { - $cmd .= ' --no-tags'; - } else { - if ($this->getTags()) { - $cmd .= ' --tags=' . escapeshellarg(implode(' ', $this->getTags())); - } - } - - passthru($cmd); - } - } - - /** - * Runs `git subsplit update` - */ - public function subsplitUpdate() - { - $repo = $this->getRepository(); - $this->log('git-subsplit update...'); - $cmd = $this->client->getCommand('subsplit'); - $cmd->addArgument('update'); - try { - $cmd->execute(); - } catch (Exception $e) { - throw new BuildException('git subsplit update failed'. $e); - } - chdir($repo . '/.subsplit'); - passthru('php ../composer.phar update --dev'); - chdir($repo); - } - - /** - * Runs `git subsplit init` based on the remote repository. - */ - public function subsplitInit() - { - $remote = $this->getRemote(); - $cmd = $this->client->getCommand('subsplit'); - $this->log('running git-subsplit init ' . $remote); - - $cmd->setArguments(array( - 'init', - $remote - )); - - try { - $output = $cmd->execute(); - } catch (Exception $e) { - throw new BuildException('git subsplit init failed'. $e); - } - $this->log(trim($output), Project::MSG_INFO); - $repo = $this->getRepository(); - chdir($repo . '/.subsplit'); - passthru('php ../composer.phar install --dev'); - chdir($repo); - } - - /** - * Find the composer.json files using Phing's directory scanner - * - * @return array - */ - protected function findSplits() - { - $this->log("checking heads for subsplits"); - $repo = $this->getRepository(); - $base = $this->getBase(); - - $splits = array(); - $heads = $this->getHeads(); - - if (!empty($base)) { - $base = '/' . ltrim($base, '/'); - } else { - $base = '/'; - } - - chdir($repo . '/.subsplit'); - foreach ($heads as $head) { - $splits[$head] = array(); - - // check each head requested *BEFORE* the actual subtree split command gets it - passthru("git checkout '$head'"); - $ds = new DirectoryScanner(); - $ds->setBasedir($repo . '/.subsplit' . $base); - $ds->setIncludes(array('**/'.$this->subIndicatorFile)); - $ds->scan(); - $files = $ds->getIncludedFiles(); - - // Process the files we found - foreach ($files as $file) { - $pkg = file_get_contents($repo . '/.subsplit' . $base .'/'. $file); - $pkg_json = json_decode($pkg, true); - $name = $pkg_json['name']; - $component = str_replace('/composer.json', '', $file); - // keep this for split cmd - $tmpreponame = explode('/', $name); - $reponame = $tmpreponame[1]; - $splits[$head][$component]['repo'] = $reponame; - $nscomponent = str_replace('/', '\\', $component); - $splits[$head][$component]['desc'] = "[READ ONLY] Subtree split of $nscomponent: " . $pkg_json['description']; - } - } - - // go back to how we found it - passthru("git checkout master"); - chdir($repo); - $this->splits = $splits; - } - - /** - * Based on list of repositories we determined we *should* have, talk - * to GitHub and make sure they're all there. - * - */ - protected function verifyRepos() - { - $this->log('verifying GitHub target repos'); - $github_org = $this->getOwningTarget()->getProject()->getProperty('github.org'); - $github_creds = $this->getOwningTarget()->getProject()->getProperty('github.basicauth'); - - if ($github_creds == 'username:password') { - $this->log('Skipping GitHub repo checks. Update github.basicauth in build.properties to verify repos.', 1); - return; - } - - $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos?type=all'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_USERPWD, $github_creds); - // change this when we know we can use our bundled CA bundle! - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - $result = curl_exec($ch); - curl_close($ch); - $repos = json_decode($result, true); - $existing_repos = array(); - - // parse out the repos we found on GitHub - foreach ($repos as $repo) { - $tmpreponame = explode('/', $repo['full_name']); - $reponame = $tmpreponame[1]; - $existing_repos[$reponame] = $repo['description']; - } - - $heads = $this->getHeads(); - foreach ($heads as $head) { - foreach ($this->splits[$head] as $component => $meta) { - - $reponame = $meta['repo']; - - if (!isset($existing_repos[$reponame])) { - $this->log("Creating missing repo $reponame"); - $payload = array( - 'name' => $reponame, - 'description' => $meta['desc'], - 'homepage' => 'http://www.guzzlephp.org/', - 'private' => true, - 'has_issues' => false, - 'has_wiki' => false, - 'has_downloads' => true, - 'auto_init' => false - ); - $ch = curl_init('https://api.github.com/orgs/'.$github_org.'/repos'); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_USERPWD, $github_creds); - curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); - // change this when we know we can use our bundled CA bundle! - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); - $result = curl_exec($ch); - echo "Response code: ".curl_getinfo($ch, CURLINFO_HTTP_CODE)."\n"; - curl_close($ch); - } else { - $this->log("Repo $reponame exists", 2); - } - } - } - } -} diff --git a/core/lib/guzzle/guzzle/phpunit.xml.dist b/core/lib/guzzle/guzzle/phpunit.xml.dist deleted file mode 100644 index 208fdc08e..000000000 --- a/core/lib/guzzle/guzzle/phpunit.xml.dist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - ./tests/Guzzle/Tests - - - - - - - - - - ./src/Guzzle - - ./src/Guzzle - ./src/Guzzle/Common/Exception/GuzzleException.php - ./src/Guzzle/Http/Exception/HttpException.php - ./src/Guzzle/Http/Exception/ServerErrorResponseException.php - ./src/Guzzle/Http/Exception/ClientErrorResponseException.php - ./src/Guzzle/Http/Exception/TooManyRedirectsException.php - ./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php - ./src/Guzzle/Common/Exception/BadMethodCallException.php - ./src/Guzzle/Common/Exception/InvalidArgumentException.php - ./src/Guzzle/Common/Exception/RuntimeException.php - ./src/Guzzle/Common/Exception/UnexpectedValueException.php - ./src/Guzzle/Service/Exception/ClientNotFoundException.php - ./src/Guzzle/Service/Exception/CommandException.php - ./src/Guzzle/Service/Exception/DescriptionBuilderException.php - ./src/Guzzle/Service/Exception/ServiceBuilderException.php - ./src/Guzzle/Service/Exception/ServiceNotFoundException.php - ./src/Guzzle/Service/Exception/ValidationException.php - ./src/Guzzle/Service/Exception/JsonException.php - - - - - diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php deleted file mode 100644 index 0625d71c3..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/AbstractBatchDecorator.php +++ /dev/null @@ -1,66 +0,0 @@ -decoratedBatch = $decoratedBatch; - } - - /** - * Allow decorators to implement custom methods - * - * @param string $method Missing method name - * @param array $args Method arguments - * - * @return mixed - * @codeCoverageIgnore - */ - public function __call($method, array $args) - { - return call_user_func_array(array($this->decoratedBatch, $method), $args); - } - - public function add($item) - { - $this->decoratedBatch->add($item); - - return $this; - } - - public function flush() - { - return $this->decoratedBatch->flush(); - } - - public function isEmpty() - { - return $this->decoratedBatch->isEmpty(); - } - - /** - * Trace the decorators associated with the batch - * - * @return array - */ - public function getDecorators() - { - $found = array($this); - if (method_exists($this->decoratedBatch, 'getDecorators')) { - $found = array_merge($found, $this->decoratedBatch->getDecorators()); - } - - return $found; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php deleted file mode 100644 index 4d41c54f8..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/Batch.php +++ /dev/null @@ -1,92 +0,0 @@ -transferStrategy = $transferStrategy; - $this->divisionStrategy = $divisionStrategy; - $this->queue = new \SplQueue(); - $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); - $this->dividedBatches = array(); - } - - public function add($item) - { - $this->queue->enqueue($item); - - return $this; - } - - public function flush() - { - $this->createBatches(); - - $items = array(); - foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { - while ($dividedBatch->valid()) { - $batch = $dividedBatch->current(); - $dividedBatch->next(); - try { - $this->transferStrategy->transfer($batch); - $items = array_merge($items, $batch); - } catch (\Exception $e) { - throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); - } - } - // Keep the divided batch down to a minimum in case of a later exception - unset($this->dividedBatches[$batchIndex]); - } - - return $items; - } - - public function isEmpty() - { - return count($this->queue) == 0 && count($this->dividedBatches) == 0; - } - - /** - * Create batches for any queued items - */ - protected function createBatches() - { - if (count($this->queue)) { - if ($batches = $this->divisionStrategy->createBatches($this->queue)) { - // Convert arrays into iterators - if (is_array($batches)) { - $batches = new \ArrayIterator($batches); - } - $this->dividedBatches[] = $batches; - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php deleted file mode 100644 index ea99b4dd0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchBuilder.php +++ /dev/null @@ -1,199 +0,0 @@ - 'Guzzle\Batch\BatchRequestTransfer', - 'command' => 'Guzzle\Batch\BatchCommandTransfer' - ); - - /** - * Create a new instance of the BatchBuilder - * - * @return BatchBuilder - */ - public static function factory() - { - return new self(); - } - - /** - * Automatically flush the batch when the size of the queue reaches a certain threshold. Adds {@see FlushingBatch}. - * - * @param $threshold Number of items to allow in the queue before a flush - * - * @return BatchBuilder - */ - public function autoFlushAt($threshold) - { - $this->autoFlush = $threshold; - - return $this; - } - - /** - * Maintain a history of all items that have been transferred using the batch. Adds {@see HistoryBatch}. - * - * @return BatchBuilder - */ - public function keepHistory() - { - $this->history = true; - - return $this; - } - - /** - * Buffer exceptions thrown during transfer so that you can transfer as much as possible, and after a transfer - * completes, inspect each exception that was thrown. Enables the {@see ExceptionBufferingBatch} decorator. - * - * @return BatchBuilder - */ - public function bufferExceptions() - { - $this->exceptionBuffering = true; - - return $this; - } - - /** - * Notify a callable each time a batch flush completes. Enables the {@see NotifyingBatch} decorator. - * - * @param mixed $callable Callable function to notify - * - * @return BatchBuilder - * @throws InvalidArgumentException if the argument is not callable - */ - public function notify($callable) - { - $this->afterFlush = $callable; - - return $this; - } - - /** - * Configures the batch to transfer batches of requests. Associates a {@see \Guzzle\Http\BatchRequestTransfer} - * object as both the transfer and divisor strategy. - * - * @param int $batchSize Batch size for each batch of requests - * - * @return BatchBuilder - */ - public function transferRequests($batchSize = 50) - { - $className = self::$mapping['request']; - $this->transferStrategy = new $className($batchSize); - $this->divisorStrategy = $this->transferStrategy; - - return $this; - } - - /** - * Configures the batch to transfer batches commands. Associates as - * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer and divisor strategy. - * - * @param int $batchSize Batch size for each batch of commands - * - * @return BatchBuilder - */ - public function transferCommands($batchSize = 50) - { - $className = self::$mapping['command']; - $this->transferStrategy = new $className($batchSize); - $this->divisorStrategy = $this->transferStrategy; - - return $this; - } - - /** - * Specify the strategy used to divide the queue into an array of batches - * - * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches - * - * @return BatchBuilder - */ - public function createBatchesWith(BatchDivisorInterface $divisorStrategy) - { - $this->divisorStrategy = $divisorStrategy; - - return $this; - } - - /** - * Specify the strategy used to transport the items when flush is called - * - * @param BatchTransferInterface $transferStrategy How items are transferred - * - * @return BatchBuilder - */ - public function transferWith(BatchTransferInterface $transferStrategy) - { - $this->transferStrategy = $transferStrategy; - - return $this; - } - - /** - * Create and return the instantiated batch - * - * @return BatchInterface - * @throws RuntimeException if no transfer strategy has been specified - */ - public function build() - { - if (!$this->transferStrategy) { - throw new RuntimeException('No transfer strategy has been specified'); - } - - if (!$this->divisorStrategy) { - throw new RuntimeException('No divisor strategy has been specified'); - } - - $batch = new Batch($this->transferStrategy, $this->divisorStrategy); - - if ($this->exceptionBuffering) { - $batch = new ExceptionBufferingBatch($batch); - } - - if ($this->afterFlush) { - $batch = new NotifyingBatch($batch, $this->afterFlush); - } - - if ($this->autoFlush) { - $batch = new FlushingBatch($batch, $this->autoFlush); - } - - if ($this->history) { - $batch = new HistoryBatch($batch); - } - - return $batch; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php deleted file mode 100644 index e0a2d9568..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureDivisor.php +++ /dev/null @@ -1,39 +0,0 @@ -callable = $callable; - $this->context = $context; - } - - public function createBatches(\SplQueue $queue) - { - return call_user_func($this->callable, $queue, $this->context); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php deleted file mode 100644 index 9cbf1aba4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchClosureTransfer.php +++ /dev/null @@ -1,40 +0,0 @@ -callable = $callable; - $this->context = $context; - } - - public function transfer(array $batch) - { - return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php deleted file mode 100644 index d55ac7d1f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchCommandTransfer.php +++ /dev/null @@ -1,75 +0,0 @@ -batchSize = $batchSize; - } - - /** - * Creates batches by grouping commands by their associated client - * {@inheritdoc} - */ - public function createBatches(\SplQueue $queue) - { - $groups = new \SplObjectStorage(); - foreach ($queue as $item) { - if (!$item instanceof CommandInterface) { - throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); - } - $client = $item->getClient(); - if (!$groups->contains($client)) { - $groups->attach($client, new \ArrayObject(array($item))); - } else { - $groups[$client]->append($item); - } - } - - $batches = array(); - foreach ($groups as $batch) { - $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); - } - - return $batches; - } - - public function transfer(array $batch) - { - if (empty($batch)) { - return; - } - - // Get the client of the first found command - $client = reset($batch)->getClient(); - - // Keep a list of all commands with invalid clients - $invalid = array_filter($batch, function ($command) use ($client) { - return $command->getClient() !== $client; - }); - - if (!empty($invalid)) { - throw new InconsistentClientTransferException($invalid); - } - - $client->execute($batch); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php deleted file mode 100644 index 0214f05f4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchDivisorInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -batchSize = $batchSize; - } - - /** - * Creates batches of requests by grouping requests by their associated curl multi object. - * {@inheritdoc} - */ - public function createBatches(\SplQueue $queue) - { - // Create batches by client objects - $groups = new \SplObjectStorage(); - foreach ($queue as $item) { - if (!$item instanceof RequestInterface) { - throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); - } - $client = $item->getClient(); - if (!$groups->contains($client)) { - $groups->attach($client, array($item)); - } else { - $current = $groups[$client]; - $current[] = $item; - $groups[$client] = $current; - } - } - - $batches = array(); - foreach ($groups as $batch) { - $batches = array_merge($batches, array_chunk($groups[$batch], $this->batchSize)); - } - - return $batches; - } - - public function transfer(array $batch) - { - if ($batch) { - reset($batch)->getClient()->send($batch); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php deleted file mode 100644 index 67f90a581..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchSizeDivisor.php +++ /dev/null @@ -1,47 +0,0 @@ -size = $size; - } - - /** - * Set the size of each batch - * - * @param int $size Size of each batch - * - * @return BatchSizeDivisor - */ - public function setSize($size) - { - $this->size = $size; - - return $this; - } - - /** - * Get the size of each batch - * - * @return int - */ - public function getSize() - { - return $this->size; - } - - public function createBatches(\SplQueue $queue) - { - return array_chunk(iterator_to_array($queue, false), $this->size); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php deleted file mode 100644 index 2e0b60dad..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/BatchTransferInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -batch = $batch; - $this->transferredItems = $transferredItems; - $this->transferStrategy = $transferStrategy; - $this->divisorStrategy = $divisorStrategy; - parent::__construct( - 'Exception encountered while transferring batch: ' . $exception->getMessage(), - $exception->getCode(), - $exception - ); - } - - /** - * Get the batch that we being sent when the exception occurred - * - * @return array - */ - public function getBatch() - { - return $this->batch; - } - - /** - * Get the items transferred at the point in which the exception was encountered - * - * @return array - */ - public function getTransferredItems() - { - return $this->transferredItems; - } - - /** - * Get the transfer strategy - * - * @return TransferStrategy - */ - public function getTransferStrategy() - { - return $this->transferStrategy; - } - - /** - * Get the divisor strategy - * - * @return DivisorStrategy - */ - public function getDivisorStrategy() - { - return $this->divisorStrategy; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php deleted file mode 100644 index d7a892885..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/ExceptionBufferingBatch.php +++ /dev/null @@ -1,50 +0,0 @@ -decoratedBatch->isEmpty()) { - try { - $transferredItems = $this->decoratedBatch->flush(); - } catch (BatchTransferException $e) { - $this->exceptions[] = $e; - $transferredItems = $e->getTransferredItems(); - } - $items = array_merge($items, $transferredItems); - } - - return $items; - } - - /** - * Get the buffered exceptions - * - * @return array Array of BatchTransferException objects - */ - public function getExceptions() - { - return $this->exceptions; - } - - /** - * Clear the buffered exceptions - */ - public function clearExceptions() - { - $this->exceptions = array(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php deleted file mode 100644 index 367b68427..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/FlushingBatch.php +++ /dev/null @@ -1,60 +0,0 @@ -threshold = $threshold; - parent::__construct($decoratedBatch); - } - - /** - * Set the auto-flush threshold - * - * @param int $threshold The auto-flush threshold - * - * @return FlushingBatch - */ - public function setThreshold($threshold) - { - $this->threshold = $threshold; - - return $this; - } - - /** - * Get the auto-flush threshold - * - * @return int - */ - public function getThreshold() - { - return $this->threshold; - } - - public function add($item) - { - $this->decoratedBatch->add($item); - if (++$this->currentTotal >= $this->threshold) { - $this->currentTotal = 0; - $this->decoratedBatch->flush(); - } - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php deleted file mode 100644 index e345fdc34..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/HistoryBatch.php +++ /dev/null @@ -1,39 +0,0 @@ -history[] = $item; - $this->decoratedBatch->add($item); - - return $this; - } - - /** - * Get the batch history - * - * @return array - */ - public function getHistory() - { - return $this->history; - } - - /** - * Clear the batch history - */ - public function clearHistory() - { - $this->history = array(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php b/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php deleted file mode 100644 index 96d04daa8..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/NotifyingBatch.php +++ /dev/null @@ -1,38 +0,0 @@ -callable = $callable; - parent::__construct($decoratedBatch); - } - - public function flush() - { - $items = $this->decoratedBatch->flush(); - call_user_func($this->callable, $items); - - return $items; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json deleted file mode 100644 index 12404d381..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Batch/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "guzzle/batch", - "description": "Guzzle batch component for batching requests, commands, or custom transfers", - "homepage": "http://guzzlephp.org/", - "keywords": ["batch", "HTTP", "REST", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Batch": "" } - }, - "suggest": { - "guzzle/http": "self.version", - "guzzle/service": "self.version" - }, - "target-dir": "Guzzle/Batch", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php deleted file mode 100644 index a5c527167..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/AbstractCacheAdapter.php +++ /dev/null @@ -1,21 +0,0 @@ -cache; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php deleted file mode 100644 index 94e623463..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterFactory.php +++ /dev/null @@ -1,117 +0,0 @@ -newInstanceArgs($args); - } - } catch (\Exception $e) { - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php deleted file mode 100644 index 970c9e228..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/CacheAdapterInterface.php +++ /dev/null @@ -1,55 +0,0 @@ -callables = $callables; - } - - public function contains($id, array $options = null) - { - return call_user_func($this->callables['contains'], $id, $options); - } - - public function delete($id, array $options = null) - { - return call_user_func($this->callables['delete'], $id, $options); - } - - public function fetch($id, array $options = null) - { - return call_user_func($this->callables['fetch'], $id, $options); - } - - public function save($id, $data, $lifeTime = false, array $options = null) - { - return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php deleted file mode 100644 index 321dd6baf..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/DoctrineCacheAdapter.php +++ /dev/null @@ -1,41 +0,0 @@ -cache = $cache; - } - - public function contains($id, array $options = null) - { - return $this->cache->contains($id); - } - - public function delete($id, array $options = null) - { - return $this->cache->delete($id); - } - - public function fetch($id, array $options = null) - { - return $this->cache->fetch($id); - } - - public function save($id, $data, $lifeTime = false, array $options = null) - { - return $this->cache->save($id, $data, $lifeTime); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php deleted file mode 100644 index 68bd4af97..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/NullCacheAdapter.php +++ /dev/null @@ -1,31 +0,0 @@ -cache = $cache; - } - - public function contains($id, array $options = null) - { - return $this->cache->test($id); - } - - public function delete($id, array $options = null) - { - return $this->cache->remove($id); - } - - public function fetch($id, array $options = null) - { - return $this->cache->load($id); - } - - public function save($id, $data, $lifeTime = false, array $options = null) - { - return $this->cache->save($data, $id, array(), $lifeTime); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php deleted file mode 100644 index 1fc18a555..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/Zf2CacheAdapter.php +++ /dev/null @@ -1,41 +0,0 @@ -cache = $cache; - } - - public function contains($id, array $options = null) - { - return $this->cache->hasItem($id); - } - - public function delete($id, array $options = null) - { - return $this->cache->removeItem($id); - } - - public function fetch($id, array $options = null) - { - return $this->cache->getItem($id); - } - - public function save($id, $data, $lifeTime = false, array $options = null) - { - return $this->cache->setItem($id, $data); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json deleted file mode 100644 index a5d999bd6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Cache/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/cache", - "description": "Guzzle cache adapter component", - "homepage": "http://guzzlephp.org/", - "keywords": ["cache", "adapter", "zf", "doctrine", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Cache": "" } - }, - "target-dir": "Guzzle/Cache", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php deleted file mode 100644 index d1e842b1c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php +++ /dev/null @@ -1,49 +0,0 @@ -eventDispatcher = $eventDispatcher; - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->eventDispatcher = new EventDispatcher(); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php deleted file mode 100644 index 5cb1535d0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Common/Collection.php +++ /dev/null @@ -1,403 +0,0 @@ -data = $data; - } - - /** - * Create a new collection from an array, validate the keys, and add default values where missing - * - * @param array $config Configuration values to apply. - * @param array $defaults Default parameters - * @param array $required Required parameter names - * - * @return self - * @throws InvalidArgumentException if a parameter is missing - */ - public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array()) - { - $data = $config + $defaults; - - if ($missing = array_diff($required, array_keys($data))) { - throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing)); - } - - return new self($data); - } - - public function count() - { - return count($this->data); - } - - public function getIterator() - { - return new \ArrayIterator($this->data); - } - - public function toArray() - { - return $this->data; - } - - /** - * Removes all key value pairs - * - * @return Collection - */ - public function clear() - { - $this->data = array(); - - return $this; - } - - /** - * Get all or a subset of matching key value pairs - * - * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs - * - * @return array Returns an array of all matching key value pairs - */ - public function getAll(array $keys = null) - { - return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data; - } - - /** - * Get a specific key value. - * - * @param string $key Key to retrieve. - * - * @return mixed|null Value of the key or NULL - */ - public function get($key) - { - return isset($this->data[$key]) ? $this->data[$key] : null; - } - - /** - * Set a key value pair - * - * @param string $key Key to set - * @param mixed $value Value to set - * - * @return Collection Returns a reference to the object - */ - public function set($key, $value) - { - $this->data[$key] = $value; - - return $this; - } - - /** - * Add a value to a key. If a key of the same name has already been added, the key value will be converted into an - * array and the new value will be pushed to the end of the array. - * - * @param string $key Key to add - * @param mixed $value Value to add to the key - * - * @return Collection Returns a reference to the object. - */ - public function add($key, $value) - { - if (!array_key_exists($key, $this->data)) { - $this->data[$key] = $value; - } elseif (is_array($this->data[$key])) { - $this->data[$key][] = $value; - } else { - $this->data[$key] = array($this->data[$key], $value); - } - - return $this; - } - - /** - * Remove a specific key value pair - * - * @param string $key A key to remove - * - * @return Collection - */ - public function remove($key) - { - unset($this->data[$key]); - - return $this; - } - - /** - * Get all keys in the collection - * - * @return array - */ - public function getKeys() - { - return array_keys($this->data); - } - - /** - * Returns whether or not the specified key is present. - * - * @param string $key The key for which to check the existence. - * - * @return bool - */ - public function hasKey($key) - { - return array_key_exists($key, $this->data); - } - - /** - * Case insensitive search the keys in the collection - * - * @param string $key Key to search for - * - * @return bool|string Returns false if not found, otherwise returns the key - */ - public function keySearch($key) - { - foreach (array_keys($this->data) as $k) { - if (!strcasecmp($k, $key)) { - return $k; - } - } - - return false; - } - - /** - * Checks if any keys contains a certain value - * - * @param string $value Value to search for - * - * @return mixed Returns the key if the value was found FALSE if the value was not found. - */ - public function hasValue($value) - { - return array_search($value, $this->data); - } - - /** - * Replace the data of the object with the value of an array - * - * @param array $data Associative array of data - * - * @return Collection Returns a reference to the object - */ - public function replace(array $data) - { - $this->data = $data; - - return $this; - } - - /** - * Add and merge in a Collection or array of key value pair data. - * - * @param Collection|array $data Associative array of key value pair data - * - * @return Collection Returns a reference to the object. - */ - public function merge($data) - { - foreach ($data as $key => $value) { - $this->add($key, $value); - } - - return $this; - } - - /** - * Over write key value pairs in this collection with all of the data from an array or collection. - * - * @param array|\Traversable $data Values to override over this config - * - * @return self - */ - public function overwriteWith($data) - { - if (is_array($data)) { - $this->data = $data + $this->data; - } elseif ($data instanceof Collection) { - $this->data = $data->toArray() + $this->data; - } else { - foreach ($data as $key => $value) { - $this->data[$key] = $value; - } - } - - return $this; - } - - /** - * Returns a Collection containing all the elements of the collection after applying the callback function to each - * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a - * modified value - * - * @param \Closure $closure Closure to apply - * @param array $context Context to pass to the closure - * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection - * - * @return Collection - */ - public function map(\Closure $closure, array $context = array(), $static = true) - { - $collection = $static ? new static() : new self(); - foreach ($this as $key => $value) { - $collection->add($key, $closure($key, $value, $context)); - } - - return $collection; - } - - /** - * Iterates over each key value pair in the collection passing them to the Closure. If the Closure function returns - * true, the current value from input is returned into the result Collection. The Closure must accept three - * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value. - * - * @param \Closure $closure Closure evaluation function - * @param bool $static Set to TRUE to use the same class as the return rather than returning a Collection - * - * @return Collection - */ - public function filter(\Closure $closure, $static = true) - { - $collection = ($static) ? new static() : new self(); - foreach ($this->data as $key => $value) { - if ($closure($key, $value)) { - $collection->add($key, $value); - } - } - - return $collection; - } - - public function offsetExists($offset) - { - return isset($this->data[$offset]); - } - - public function offsetGet($offset) - { - return isset($this->data[$offset]) ? $this->data[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->data[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->data[$offset]); - } - - /** - * Set a value into a nested array key. Keys will be created as needed to set the value. - * - * @param string $path Path to set - * @param mixed $value Value to set at the key - * - * @return self - * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value - */ - public function setPath($path, $value) - { - $current =& $this->data; - $queue = explode('/', $path); - while (null !== ($key = array_shift($queue))) { - if (!is_array($current)) { - throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array"); - } elseif (!$queue) { - $current[$key] = $value; - } elseif (isset($current[$key])) { - $current =& $current[$key]; - } else { - $current[$key] = array(); - $current =& $current[$key]; - } - } - - return $this; - } - - /** - * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays) - * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This - * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path. - * - * @param string $path Path to traverse and retrieve a value from - * @param string $separator Character used to add depth to the search - * @param mixed $data Optional data to descend into (used when wildcards are encountered) - * - * @return mixed|null - */ - public function getPath($path, $separator = '/', $data = null) - { - if ($data === null) { - $data =& $this->data; - } - - $path = is_array($path) ? $path : explode($separator, $path); - while (null !== ($part = array_shift($path))) { - if (!is_array($data)) { - return null; - } elseif (isset($data[$part])) { - $data =& $data[$part]; - } elseif ($part != '*') { - return null; - } else { - // Perform a wildcard search by diverging and merging paths - $result = array(); - foreach ($data as $value) { - if (!$path) { - $result = array_merge_recursive($result, (array) $value); - } elseif (null !== ($test = $this->getPath($path, $separator, $value))) { - $result = array_merge_recursive($result, (array) $test); - } - } - return $result; - } - } - - return $data; - } - - /** - * Inject configuration settings into an input string - * - * @param string $input Input to inject - * - * @return string - * @deprecated - */ - public function inject($input) - { - Version::warn(__METHOD__ . ' is deprecated'); - $replace = array(); - foreach ($this->data as $key => $val) { - $replace['{' . $key . '}'] = $val; - } - - return strtr($input, $replace); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php deleted file mode 100644 index fad76a9b8..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Common/Event.php +++ /dev/null @@ -1,52 +0,0 @@ -context = $context; - } - - public function getIterator() - { - return new \ArrayIterator($this->context); - } - - public function offsetGet($offset) - { - return isset($this->context[$offset]) ? $this->context[$offset] : null; - } - - public function offsetSet($offset, $value) - { - $this->context[$offset] = $value; - } - - public function offsetExists($offset) - { - return isset($this->context[$offset]); - } - - public function offsetUnset($offset) - { - unset($this->context[$offset]); - } - - public function toArray() - { - return $this->context; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php deleted file mode 100644 index 08d1c7256..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php +++ /dev/null @@ -1,5 +0,0 @@ -shortMessage = $message; - } - - /** - * Set all of the exceptions - * - * @param array $exceptions Array of exceptions - * - * @return self - */ - public function setExceptions(array $exceptions) - { - $this->exceptions = array(); - foreach ($exceptions as $exception) { - $this->add($exception); - } - - return $this; - } - - /** - * Add exceptions to the collection - * - * @param ExceptionCollection|\Exception $e Exception to add - * - * @return ExceptionCollection; - */ - public function add($e) - { - $this->exceptions[] = $e; - if ($this->message) { - $this->message .= "\n"; - } - - $this->message .= $this->getExceptionMessage($e, 0); - - return $this; - } - - /** - * Get the total number of request exceptions - * - * @return int - */ - public function count() - { - return count($this->exceptions); - } - - /** - * Allows array-like iteration over the request exceptions - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new \ArrayIterator($this->exceptions); - } - - /** - * Get the first exception in the collection - * - * @return \Exception - */ - public function getFirst() - { - return $this->exceptions ? $this->exceptions[0] : null; - } - - private function getExceptionMessage(\Exception $e, $depth = 0) - { - static $sp = ' '; - $prefix = $depth ? str_repeat($sp, $depth) : ''; - $message = "{$prefix}(" . get_class($e) . ') ' . $e->getFile() . ' line ' . $e->getLine() . "\n"; - - if ($e instanceof self) { - if ($e->shortMessage) { - $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->shortMessage) . "\n"; - } - foreach ($e as $ee) { - $message .= "\n" . $this->getExceptionMessage($ee, $depth + 1); - } - } else { - $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getMessage()) . "\n"; - $message .= "\n{$prefix}{$sp}" . str_replace("\n", "\n{$prefix}{$sp}", $e->getTraceAsString()) . "\n"; - } - - return str_replace(getcwd(), '.', $message); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php b/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php deleted file mode 100644 index 458e6f2ea..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Common/Exception/GuzzleException.php +++ /dev/null @@ -1,8 +0,0 @@ -=5.3.2", - "symfony/event-dispatcher": ">=2.1" - }, - "autoload": { - "psr-0": { "Guzzle\\Common": "" } - }, - "target-dir": "Guzzle/Common", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php deleted file mode 100644 index 5005a887c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/AbstractEntityBodyDecorator.php +++ /dev/null @@ -1,221 +0,0 @@ -body = $body; - } - - public function __toString() - { - return (string) $this->body; - } - - /** - * Allow decorators to implement custom methods - * - * @param string $method Missing method name - * @param array $args Method arguments - * - * @return mixed - */ - public function __call($method, array $args) - { - return call_user_func_array(array($this->body, $method), $args); - } - - public function close() - { - return $this->body->close(); - } - - public function setRewindFunction($callable) - { - $this->body->setRewindFunction($callable); - - return $this; - } - - public function rewind() - { - return $this->body->rewind(); - } - - public function compress($filter = 'zlib.deflate') - { - return $this->body->compress($filter); - } - - public function uncompress($filter = 'zlib.inflate') - { - return $this->body->uncompress($filter); - } - - public function getContentLength() - { - return $this->getSize(); - } - - public function getContentType() - { - return $this->body->getContentType(); - } - - public function getContentMd5($rawOutput = false, $base64Encode = false) - { - $hash = Stream::getHash($this, 'md5', $rawOutput); - - return $hash && $base64Encode ? base64_encode($hash) : $hash; - } - - public function getContentEncoding() - { - return $this->body->getContentEncoding(); - } - - public function getMetaData($key = null) - { - return $this->body->getMetaData($key); - } - - public function getStream() - { - return $this->body->getStream(); - } - - public function setStream($stream, $size = 0) - { - $this->body->setStream($stream, $size); - - return $this; - } - - public function detachStream() - { - $this->body->detachStream(); - - return $this; - } - - public function getWrapper() - { - return $this->body->getWrapper(); - } - - public function getWrapperData() - { - return $this->body->getWrapperData(); - } - - public function getStreamType() - { - return $this->body->getStreamType(); - } - - public function getUri() - { - return $this->body->getUri(); - } - - public function getSize() - { - return $this->body->getSize(); - } - - public function isReadable() - { - return $this->body->isReadable(); - } - - public function isRepeatable() - { - return $this->isSeekable() && $this->isReadable(); - } - - public function isWritable() - { - return $this->body->isWritable(); - } - - public function isConsumed() - { - return $this->body->isConsumed(); - } - - /** - * Alias of isConsumed() - * {@inheritdoc} - */ - public function feof() - { - return $this->isConsumed(); - } - - public function isLocal() - { - return $this->body->isLocal(); - } - - public function isSeekable() - { - return $this->body->isSeekable(); - } - - public function setSize($size) - { - $this->body->setSize($size); - - return $this; - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->body->seek($offset, $whence); - } - - public function read($length) - { - return $this->body->read($length); - } - - public function write($string) - { - return $this->body->write($string); - } - - public function readLine($maxLength = null) - { - return $this->body->readLine($maxLength); - } - - public function ftell() - { - return $this->body->ftell(); - } - - public function getCustomData($key) - { - return $this->body->getCustomData($key); - } - - public function setCustomData($key, $value) - { - $this->body->setCustomData($key, $value); - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php deleted file mode 100644 index c65c13650..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/CachingEntityBody.php +++ /dev/null @@ -1,229 +0,0 @@ -remoteStream = $body; - $this->body = new EntityBody(fopen('php://temp', 'r+')); - } - - /** - * Will give the contents of the buffer followed by the exhausted remote stream. - * - * Warning: Loads the entire stream into memory - * - * @return string - */ - public function __toString() - { - $pos = $this->ftell(); - $this->rewind(); - - $str = ''; - while (!$this->isConsumed()) { - $str .= $this->read(16384); - } - - $this->seek($pos); - - return $str; - } - - public function getSize() - { - return max($this->body->getSize(), $this->remoteStream->getSize()); - } - - /** - * {@inheritdoc} - * @throws RuntimeException When seeking with SEEK_END or when seeking past the total size of the buffer stream - */ - public function seek($offset, $whence = SEEK_SET) - { - if ($whence == SEEK_SET) { - $byte = $offset; - } elseif ($whence == SEEK_CUR) { - $byte = $offset + $this->ftell(); - } else { - throw new RuntimeException(__CLASS__ . ' supports only SEEK_SET and SEEK_CUR seek operations'); - } - - // You cannot skip ahead past where you've read from the remote stream - if ($byte > $this->body->getSize()) { - throw new RuntimeException( - "Cannot seek to byte {$byte} when the buffered stream only contains {$this->body->getSize()} bytes" - ); - } - - return $this->body->seek($byte); - } - - public function rewind() - { - return $this->seek(0); - } - - /** - * Does not support custom rewind functions - * - * @throws RuntimeException - */ - public function setRewindFunction($callable) - { - throw new RuntimeException(__CLASS__ . ' does not support custom stream rewind functions'); - } - - public function read($length) - { - // Perform a regular read on any previously read data from the buffer - $data = $this->body->read($length); - $remaining = $length - strlen($data); - - // More data was requested so read from the remote stream - if ($remaining) { - // If data was written to the buffer in a position that would have been filled from the remote stream, - // then we must skip bytes on the remote stream to emulate overwriting bytes from that position. This - // mimics the behavior of other PHP stream wrappers. - $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); - - if ($this->skipReadBytes) { - $len = strlen($remoteData); - $remoteData = substr($remoteData, $this->skipReadBytes); - $this->skipReadBytes = max(0, $this->skipReadBytes - $len); - } - - $data .= $remoteData; - $this->body->write($remoteData); - } - - return $data; - } - - public function write($string) - { - // When appending to the end of the currently read stream, you'll want to skip bytes from being read from - // the remote stream to emulate other stream wrappers. Basically replacing bytes of data of a fixed length. - $overflow = (strlen($string) + $this->ftell()) - $this->remoteStream->ftell(); - if ($overflow > 0) { - $this->skipReadBytes += $overflow; - } - - return $this->body->write($string); - } - - /** - * {@inheritdoc} - * @link http://php.net/manual/en/function.fgets.php - */ - public function readLine($maxLength = null) - { - $buffer = ''; - $size = 0; - while (!$this->isConsumed()) { - $byte = $this->read(1); - $buffer .= $byte; - // Break when a new line is found or the max length - 1 is reached - if ($byte == PHP_EOL || ++$size == $maxLength - 1) { - break; - } - } - - return $buffer; - } - - public function isConsumed() - { - return $this->body->isConsumed() && $this->remoteStream->isConsumed(); - } - - /** - * Close both the remote stream and buffer stream - */ - public function close() - { - return $this->remoteStream->close() && $this->body->close(); - } - - public function setStream($stream, $size = 0) - { - $this->remoteStream->setStream($stream, $size); - } - - public function getContentType() - { - return $this->remoteStream->getContentType(); - } - - public function getContentEncoding() - { - return $this->remoteStream->getContentEncoding(); - } - - public function getMetaData($key = null) - { - return $this->remoteStream->getMetaData($key); - } - - public function getStream() - { - return $this->remoteStream->getStream(); - } - - public function getWrapper() - { - return $this->remoteStream->getWrapper(); - } - - public function getWrapperData() - { - return $this->remoteStream->getWrapperData(); - } - - public function getStreamType() - { - return $this->remoteStream->getStreamType(); - } - - public function getUri() - { - return $this->remoteStream->getUri(); - } - - /** - * Always retrieve custom data from the remote stream - * {@inheritdoc} - */ - public function getCustomData($key) - { - return $this->remoteStream->getCustomData($key); - } - - /** - * Always set custom data on the remote stream - * {@inheritdoc} - */ - public function setCustomData($key, $value) - { - $this->remoteStream->setCustomData($key, $value); - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php deleted file mode 100644 index 22df2ec6e..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Client.php +++ /dev/null @@ -1,490 +0,0 @@ -setConfig($config ?: new Collection()); - $this->initSsl(); - $this->setBaseUrl($baseUrl); - $this->defaultHeaders = new Collection(); - $this->setRequestFactory(RequestFactory::getInstance()); - $this->userAgent = $this->getDefaultUserAgent(); - if (!$this->config[self::DISABLE_REDIRECTS]) { - $this->addSubscriber(new RedirectPlugin()); - } - } - - final public function setConfig($config) - { - if ($config instanceof Collection) { - $this->config = $config; - } elseif (is_array($config)) { - $this->config = new Collection($config); - } else { - throw new InvalidArgumentException('Config must be an array or Collection'); - } - - return $this; - } - - final public function getConfig($key = false) - { - return $key ? $this->config[$key] : $this->config; - } - - /** - * Set a default request option on the client that will be used as a default for each request - * - * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) - * @param mixed $value Value to set - * - * @return $this - */ - public function setDefaultOption($keyOrPath, $value) - { - $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; - $this->config->setPath($keyOrPath, $value); - - return $this; - } - - /** - * Retrieve a default request option from the client - * - * @param string $keyOrPath request.options key (e.g. allow_redirects) or path to a nested key (e.g. headers/foo) - * - * @return mixed|null - */ - public function getDefaultOption($keyOrPath) - { - $keyOrPath = self::REQUEST_OPTIONS . '/' . $keyOrPath; - - return $this->config->getPath($keyOrPath); - } - - final public function setSslVerification($certificateAuthority = true, $verifyPeer = true, $verifyHost = 2) - { - $opts = $this->config[self::CURL_OPTIONS] ?: array(); - - if ($certificateAuthority === true) { - // use bundled CA bundle, set secure defaults - $opts[CURLOPT_CAINFO] = __DIR__ . '/Resources/cacert.pem'; - $opts[CURLOPT_SSL_VERIFYPEER] = true; - $opts[CURLOPT_SSL_VERIFYHOST] = 2; - } elseif ($certificateAuthority === false) { - unset($opts[CURLOPT_CAINFO]); - $opts[CURLOPT_SSL_VERIFYPEER] = false; - $opts[CURLOPT_SSL_VERIFYHOST] = 0; - } elseif ($verifyPeer !== true && $verifyPeer !== false && $verifyPeer !== 1 && $verifyPeer !== 0) { - throw new InvalidArgumentException('verifyPeer must be 1, 0 or boolean'); - } elseif ($verifyHost !== 0 && $verifyHost !== 1 && $verifyHost !== 2) { - throw new InvalidArgumentException('verifyHost must be 0, 1 or 2'); - } else { - $opts[CURLOPT_SSL_VERIFYPEER] = $verifyPeer; - $opts[CURLOPT_SSL_VERIFYHOST] = $verifyHost; - if (is_file($certificateAuthority)) { - unset($opts[CURLOPT_CAPATH]); - $opts[CURLOPT_CAINFO] = $certificateAuthority; - } elseif (is_dir($certificateAuthority)) { - unset($opts[CURLOPT_CAINFO]); - $opts[CURLOPT_CAPATH] = $certificateAuthority; - } else { - throw new RuntimeException( - 'Invalid option passed to ' . self::SSL_CERT_AUTHORITY . ': ' . $certificateAuthority - ); - } - } - - $this->config->set(self::CURL_OPTIONS, $opts); - - return $this; - } - - public function createRequest($method = 'GET', $uri = null, $headers = null, $body = null, array $options = array()) - { - if (!$uri) { - $url = $this->getBaseUrl(); - } else { - if (!is_array($uri)) { - $templateVars = null; - } else { - list($uri, $templateVars) = $uri; - } - if (strpos($uri, '://')) { - // Use absolute URLs as-is - $url = $this->expandTemplate($uri, $templateVars); - } else { - $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); - } - } - - // If default headers are provided, then merge them under any explicitly provided headers for the request - if (count($this->defaultHeaders)) { - if (!$headers) { - $headers = $this->defaultHeaders->toArray(); - } elseif (is_array($headers)) { - $headers += $this->defaultHeaders->toArray(); - } elseif ($headers instanceof Collection) { - $headers = $headers->toArray() + $this->defaultHeaders->toArray(); - } - } - - return $this->prepareRequest($this->requestFactory->create($method, (string) $url, $headers, $body), $options); - } - - public function getBaseUrl($expand = true) - { - return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; - } - - public function setBaseUrl($url) - { - $this->baseUrl = $url; - - return $this; - } - - public function setUserAgent($userAgent, $includeDefault = false) - { - if ($includeDefault) { - $userAgent .= ' ' . $this->getDefaultUserAgent(); - } - $this->userAgent = $userAgent; - - return $this; - } - - /** - * Get the default User-Agent string to use with Guzzle - * - * @return string - */ - public function getDefaultUserAgent() - { - return 'Guzzle/' . Version::VERSION - . ' curl/' . CurlVersion::getInstance()->get('version') - . ' PHP/' . PHP_VERSION; - } - - public function get($uri = null, $headers = null, $options = array()) - { - // BC compat: $options can be a string, resource, etc to specify where the response body is downloaded - return is_array($options) - ? $this->createRequest('GET', $uri, $headers, null, $options) - : $this->createRequest('GET', $uri, $headers, $options); - } - - public function head($uri = null, $headers = null, array $options = array()) - { - return $this->createRequest('HEAD', $uri, $headers, null, $options); - } - - public function delete($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('DELETE', $uri, $headers, $body, $options); - } - - public function put($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('PUT', $uri, $headers, $body, $options); - } - - public function patch($uri = null, $headers = null, $body = null, array $options = array()) - { - return $this->createRequest('PATCH', $uri, $headers, $body, $options); - } - - public function post($uri = null, $headers = null, $postBody = null, array $options = array()) - { - return $this->createRequest('POST', $uri, $headers, $postBody, $options); - } - - public function options($uri = null, array $options = array()) - { - return $this->createRequest('OPTIONS', $uri, $options); - } - - public function send($requests) - { - if (!($requests instanceof RequestInterface)) { - return $this->sendMultiple($requests); - } - - try { - /** @var $requests RequestInterface */ - $this->getCurlMulti()->add($requests)->send(); - return $requests->getResponse(); - } catch (ExceptionCollection $e) { - throw $e->getFirst(); - } - } - - /** - * Set a curl multi object to be used internally by the client for transferring requests. - * - * @param CurlMultiInterface $curlMulti Multi object - * - * @return self - */ - public function setCurlMulti(CurlMultiInterface $curlMulti) - { - $this->curlMulti = $curlMulti; - - return $this; - } - - /** - * @return CurlMultiInterface|CurlMultiProxy - */ - public function getCurlMulti() - { - if (!$this->curlMulti) { - $this->curlMulti = new CurlMultiProxy( - self::MAX_HANDLES, - $this->getConfig('select_timeout') ?: self::DEFAULT_SELECT_TIMEOUT - ); - } - - return $this->curlMulti; - } - - public function setRequestFactory(RequestFactoryInterface $factory) - { - $this->requestFactory = $factory; - - return $this; - } - - /** - * Set the URI template expander to use with the client - * - * @param UriTemplateInterface $uriTemplate URI template expander - * - * @return self - */ - public function setUriTemplate(UriTemplateInterface $uriTemplate) - { - $this->uriTemplate = $uriTemplate; - - return $this; - } - - /** - * Expand a URI template while merging client config settings into the template variables - * - * @param string $template Template to expand - * @param array $variables Variables to inject - * - * @return string - */ - protected function expandTemplate($template, array $variables = null) - { - $expansionVars = $this->getConfig()->toArray(); - if ($variables) { - $expansionVars = $variables + $expansionVars; - } - - return $this->getUriTemplate()->expand($template, $expansionVars); - } - - /** - * Get the URI template expander used by the client - * - * @return UriTemplateInterface - */ - protected function getUriTemplate() - { - if (!$this->uriTemplate) { - $this->uriTemplate = ParserRegistry::getInstance()->getParser('uri_template'); - } - - return $this->uriTemplate; - } - - /** - * Send multiple requests in parallel - * - * @param array $requests Array of RequestInterface objects - * - * @return array Returns an array of Response objects - */ - protected function sendMultiple(array $requests) - { - $curlMulti = $this->getCurlMulti(); - foreach ($requests as $request) { - $curlMulti->add($request); - } - $curlMulti->send(); - - /** @var $request RequestInterface */ - $result = array(); - foreach ($requests as $request) { - $result[] = $request->getResponse(); - } - - return $result; - } - - /** - * Prepare a request to be sent from the Client by adding client specific behaviors and properties to the request. - * - * @param RequestInterface $request Request to prepare for the client - * @param array $options Options to apply to the request - * - * @return RequestInterface - */ - protected function prepareRequest(RequestInterface $request, array $options = array()) - { - $request->setClient($this)->setEventDispatcher(clone $this->getEventDispatcher()); - - if ($curl = $this->config[self::CURL_OPTIONS]) { - $request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($curl)); - } - - if ($params = $this->config[self::REQUEST_PARAMS]) { - Version::warn('request.params is deprecated. Use request.options to add default request options.'); - $request->getParams()->overwriteWith($params); - } - - if ($this->userAgent && !$request->hasHeader('User-Agent')) { - $request->setHeader('User-Agent', $this->userAgent); - } - - if ($defaults = $this->config[self::REQUEST_OPTIONS]) { - $this->requestFactory->applyOptions($request, $defaults, RequestFactoryInterface::OPTIONS_AS_DEFAULTS); - } - - if ($options) { - $this->requestFactory->applyOptions($request, $options); - } - - $this->dispatch('client.create_request', array('client' => $this, 'request' => $request)); - - return $request; - } - - /** - * Initializes SSL settings - */ - protected function initSsl() - { - $authority = $this->config[self::SSL_CERT_AUTHORITY]; - - if ($authority === 'system') { - return; - } - - if ($authority === null) { - $authority = true; - } - - if ($authority === true && substr(__FILE__, 0, 7) == 'phar://') { - $authority = sys_get_temp_dir() . '/guzzle-cacert.pem'; - } - - $this->setSslVerification($authority); - } - - /** - * @deprecated - */ - public function getDefaultHeaders() - { - Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to retrieve default request options'); - return $this->defaultHeaders; - } - - /** - * @deprecated - */ - public function setDefaultHeaders($headers) - { - Version::warn(__METHOD__ . ' is deprecated. Use the request.options array to specify default request options'); - if ($headers instanceof Collection) { - $this->defaultHeaders = $headers; - } elseif (is_array($headers)) { - $this->defaultHeaders = new Collection($headers); - } else { - throw new InvalidArgumentException('Headers must be an array or Collection'); - } - - return $this; - } - - /** - * @deprecated - */ - public function preparePharCacert($md5Check = true) - { - return sys_get_temp_dir() . '/guzzle-cacert.pem'; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php deleted file mode 100644 index 10e4de2ab..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php +++ /dev/null @@ -1,223 +0,0 @@ -getCurlOptions(); - $mediator = new RequestMediator($request, $requestCurlOptions->get('emit_io')); - $tempContentLength = null; - $method = $request->getMethod(); - $bodyAsString = $requestCurlOptions->get(self::BODY_AS_STRING); - - // Prepare url - $url = (string)$request->getUrl(); - if(($pos = strpos($url, '#')) !== false ){ - // strip fragment from url - $url = substr($url, 0, $pos); - } - - // Array of default cURL options. - $curlOptions = array( - CURLOPT_URL => $url, - CURLOPT_CONNECTTIMEOUT => 150, - CURLOPT_RETURNTRANSFER => false, - CURLOPT_HEADER => false, - CURLOPT_PORT => $request->getPort(), - CURLOPT_HTTPHEADER => array(), - CURLOPT_WRITEFUNCTION => array($mediator, 'writeResponseBody'), - CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader'), - CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' - ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, - // Verifies the authenticity of the peer's certificate - CURLOPT_SSL_VERIFYPEER => 1, - // Certificate must indicate that the server is the server to which you meant to connect - CURLOPT_SSL_VERIFYHOST => 2 - ); - - if (defined('CURLOPT_PROTOCOLS')) { - // Allow only HTTP and HTTPS protocols - $curlOptions[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS; - } - - // Add CURLOPT_ENCODING if Accept-Encoding header is provided - if ($acceptEncodingHeader = $request->getHeader('Accept-Encoding')) { - $curlOptions[CURLOPT_ENCODING] = (string) $acceptEncodingHeader; - // Let cURL set the Accept-Encoding header, prevents duplicate values - $request->removeHeader('Accept-Encoding'); - } - - // Enable curl debug information if the 'debug' param was set - if ($requestCurlOptions->get('debug')) { - $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); - // @codeCoverageIgnoreStart - if (false === $curlOptions[CURLOPT_STDERR]) { - throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); - } - // @codeCoverageIgnoreEnd - $curlOptions[CURLOPT_VERBOSE] = true; - } - - // Specify settings according to the HTTP method - if ($method == 'GET') { - $curlOptions[CURLOPT_HTTPGET] = true; - } elseif ($method == 'HEAD') { - $curlOptions[CURLOPT_NOBODY] = true; - // HEAD requests do not use a write function - unset($curlOptions[CURLOPT_WRITEFUNCTION]); - } elseif (!($request instanceof EntityEnclosingRequest)) { - $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; - } else { - - $curlOptions[CURLOPT_CUSTOMREQUEST] = $method; - - // Handle sending raw bodies in a request - if ($request->getBody()) { - // You can send the body as a string using curl's CURLOPT_POSTFIELDS - if ($bodyAsString) { - $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getBody(); - // Allow curl to add the Content-Length for us to account for the times when - // POST redirects are followed by GET requests - if ($tempContentLength = $request->getHeader('Content-Length')) { - $tempContentLength = (int) (string) $tempContentLength; - } - // Remove the curl generated Content-Type header if none was set manually - if (!$request->hasHeader('Content-Type')) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Content-Type:'; - } - } else { - $curlOptions[CURLOPT_UPLOAD] = true; - // Let cURL handle setting the Content-Length header - if ($tempContentLength = $request->getHeader('Content-Length')) { - $tempContentLength = (int) (string) $tempContentLength; - $curlOptions[CURLOPT_INFILESIZE] = $tempContentLength; - } - // Add a callback for curl to read data to send with the request only if a body was specified - $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); - // Attempt to seek to the start of the stream - $request->getBody()->seek(0); - } - - } else { - - // Special handling for POST specific fields and files - $postFields = false; - if (count($request->getPostFiles())) { - $postFields = $request->getPostFields()->useUrlEncoding(false)->urlEncode(); - foreach ($request->getPostFiles() as $key => $data) { - $prefixKeys = count($data) > 1; - foreach ($data as $index => $file) { - // Allow multiple files in the same key - $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; - $postFields[$fieldKey] = $file->getCurlValue(); - } - } - } elseif (count($request->getPostFields())) { - $postFields = (string) $request->getPostFields()->useUrlEncoding(true); - } - - if ($postFields !== false) { - if ($method == 'POST') { - unset($curlOptions[CURLOPT_CUSTOMREQUEST]); - $curlOptions[CURLOPT_POST] = true; - } - $curlOptions[CURLOPT_POSTFIELDS] = $postFields; - $request->removeHeader('Content-Length'); - } - } - - // If the Expect header is not present, prevent curl from adding it - if (!$request->hasHeader('Expect')) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; - } - } - - // If a Content-Length header was specified but we want to allow curl to set one for us - if (null !== $tempContentLength) { - $request->removeHeader('Content-Length'); - } - - // Set custom cURL options - foreach ($requestCurlOptions->toArray() as $key => $value) { - if (is_numeric($key)) { - $curlOptions[$key] = $value; - } - } - - // Do not set an Accept header by default - if (!isset($curlOptions[CURLOPT_ENCODING])) { - $curlOptions[CURLOPT_HTTPHEADER][] = 'Accept:'; - } - - // Add any custom headers to the request. Empty headers will cause curl to not send the header at all. - foreach ($request->getHeaderLines() as $line) { - $curlOptions[CURLOPT_HTTPHEADER][] = $line; - } - - // Add the content-length header back if it was temporarily removed - if ($tempContentLength) { - $request->setHeader('Content-Length', $tempContentLength); - } - - // Apply the options to a new cURL handle. - $handle = curl_init(); - - // Enable the progress function if the 'progress' param was set - if ($requestCurlOptions->get('progress')) { - // Wrap the function in a function that provides the curl handle to the mediator's progress function - // Using this rather than injecting the handle into the mediator prevents a circular reference - $curlOptions[CURLOPT_PROGRESSFUNCTION] = function () use ($mediator, $handle) { - $args = func_get_args(); - $args[] = $handle; - - // PHP 5.5 pushed the handle onto the start of the args - if (is_resource($args[0])) { - array_shift($args); - } - - call_user_func_array(array($mediator, 'progress'), $args); - }; - $curlOptions[CURLOPT_NOPROGRESS] = false; - } - - curl_setopt_array($handle, $curlOptions); - - return new static($handle, $curlOptions); - } - - /** - * Construct a new CurlHandle object that wraps a cURL handle - * - * @param resource $handle Configured cURL handle resource - * @param Collection|array $options Curl options to use with the handle - * - * @throws InvalidArgumentException - */ - public function __construct($handle, $options) - { - if (!is_resource($handle)) { - throw new InvalidArgumentException('Invalid handle provided'); - } - if (is_array($options)) { - $this->options = new Collection($options); - } elseif ($options instanceof Collection) { - $this->options = $options; - } else { - throw new InvalidArgumentException('Expected array or Collection'); - } - $this->handle = $handle; - } - - /** - * Destructor - */ - public function __destruct() - { - $this->close(); - } - - /** - * Close the curl handle - */ - public function close() - { - if (is_resource($this->handle)) { - curl_close($this->handle); - } - $this->handle = null; - } - - /** - * Check if the handle is available and still OK - * - * @return bool - */ - public function isAvailable() - { - return is_resource($this->handle); - } - - /** - * Get the last error that occurred on the cURL handle - * - * @return string - */ - public function getError() - { - return $this->isAvailable() ? curl_error($this->handle) : ''; - } - - /** - * Get the last error number that occurred on the cURL handle - * - * @return int - */ - public function getErrorNo() - { - if ($this->errorNo) { - return $this->errorNo; - } - - return $this->isAvailable() ? curl_errno($this->handle) : CURLE_OK; - } - - /** - * Set the curl error number - * - * @param int $error Error number to set - * - * @return CurlHandle - */ - public function setErrorNo($error) - { - $this->errorNo = $error; - - return $this; - } - - /** - * Get cURL curl_getinfo data - * - * @param int $option Option to retrieve. Pass null to retrieve all data as an array. - * - * @return array|mixed - */ - public function getInfo($option = null) - { - if (!is_resource($this->handle)) { - return null; - } - - if (null !== $option) { - return curl_getinfo($this->handle, $option) ?: null; - } - - return curl_getinfo($this->handle) ?: array(); - } - - /** - * Get the stderr output - * - * @param bool $asResource Set to TRUE to get an fopen resource - * - * @return string|resource|null - */ - public function getStderr($asResource = false) - { - $stderr = $this->getOptions()->get(CURLOPT_STDERR); - if (!$stderr) { - return null; - } - - if ($asResource) { - return $stderr; - } - - fseek($stderr, 0); - $e = stream_get_contents($stderr); - fseek($stderr, 0, SEEK_END); - - return $e; - } - - /** - * Get the URL that this handle is connecting to - * - * @return Url - */ - public function getUrl() - { - return Url::factory($this->options->get(CURLOPT_URL)); - } - - /** - * Get the wrapped curl handle - * - * @return resource|null Returns the cURL handle or null if it was closed - */ - public function getHandle() - { - return $this->isAvailable() ? $this->handle : null; - } - - /** - * Get the cURL setopt options of the handle. Changing values in the return object will have no effect on the curl - * handle after it is created. - * - * @return Collection - */ - public function getOptions() - { - return $this->options; - } - - /** - * Update a request based on the log messages of the CurlHandle - * - * @param RequestInterface $request Request to update - */ - public function updateRequestFromTransfer(RequestInterface $request) - { - if (!$request->getResponse()) { - return; - } - - // Update the transfer stats of the response - $request->getResponse()->setInfo($this->getInfo()); - - if (!$log = $this->getStderr(true)) { - return; - } - - // Parse the cURL stderr output for outgoing requests - $headers = ''; - fseek($log, 0); - while (($line = fgets($log)) !== false) { - if ($line && $line[0] == '>') { - $headers = substr(trim($line), 2) . "\r\n"; - while (($line = fgets($log)) !== false) { - if ($line[0] == '*' || $line[0] == '<') { - break; - } else { - $headers .= trim($line) . "\r\n"; - } - } - } - } - - // Add request headers to the request exactly as they were sent - if ($headers) { - $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($headers); - if (!empty($parsed['headers'])) { - $request->setHeaders(array()); - foreach ($parsed['headers'] as $name => $value) { - $request->setHeader($name, $value); - } - } - if (!empty($parsed['version'])) { - $request->setProtocolVersion($parsed['version']); - } - } - } - - /** - * Parse the config and replace curl.* configurators into the constant based values so it can be used elsewhere - * - * @param array|Collection $config The configuration we want to parse - * - * @return array - */ - public static function parseCurlConfig($config) - { - $curlOptions = array(); - foreach ($config as $key => $value) { - if (is_string($key) && defined($key)) { - // Convert constants represented as string to constant int values - $key = constant($key); - } - if (is_string($value) && defined($value)) { - $value = constant($value); - } - $curlOptions[$key] = $value; - } - - return $curlOptions; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php deleted file mode 100644 index e8301be34..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php +++ /dev/null @@ -1,367 +0,0 @@ - array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), - CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), - CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), - CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') - ); - - /** @var float */ - protected $selectTimeout; - - public function __construct($selectTimeout = 1.0) - { - $this->selectTimeout = $selectTimeout; - $this->multiHandle = curl_multi_init(); - // @codeCoverageIgnoreStart - if ($this->multiHandle === false) { - throw new CurlException('Unable to create multi handle'); - } - // @codeCoverageIgnoreEnd - $this->reset(); - } - - public function __destruct() - { - if (is_resource($this->multiHandle)) { - curl_multi_close($this->multiHandle); - } - } - - public function add(RequestInterface $request) - { - $this->requests[] = $request; - // If requests are currently transferring and this is async, then the - // request must be prepared now as the send() method is not called. - $this->beforeSend($request); - $this->dispatch(self::ADD_REQUEST, array('request' => $request)); - - return $this; - } - - public function all() - { - return $this->requests; - } - - public function remove(RequestInterface $request) - { - $this->removeHandle($request); - if (($index = array_search($request, $this->requests, true)) !== false) { - $request = $this->requests[$index]; - unset($this->requests[$index]); - $this->requests = array_values($this->requests); - $this->dispatch(self::REMOVE_REQUEST, array('request' => $request)); - return true; - } - - return false; - } - - public function reset($hard = false) - { - // Remove each request - if ($this->requests) { - foreach ($this->requests as $request) { - $this->remove($request); - } - } - - $this->handles = new \SplObjectStorage(); - $this->requests = $this->resourceHash = $this->exceptions = $this->successful = array(); - } - - public function send() - { - $this->perform(); - $exceptions = $this->exceptions; - $successful = $this->successful; - $this->reset(); - - if ($exceptions) { - $this->throwMultiException($exceptions, $successful); - } - } - - public function count() - { - return count($this->requests); - } - - /** - * Build and throw a MultiTransferException - * - * @param array $exceptions Exceptions encountered - * @param array $successful Successful requests - * @throws MultiTransferException - */ - protected function throwMultiException(array $exceptions, array $successful) - { - $multiException = new MultiTransferException('Errors during multi transfer'); - - while ($e = array_shift($exceptions)) { - $multiException->addFailedRequestWithException($e['request'], $e['exception']); - } - - // Add successful requests - foreach ($successful as $request) { - if (!$multiException->containsRequest($request)) { - $multiException->addSuccessfulRequest($request); - } - } - - throw $multiException; - } - - /** - * Prepare for sending - * - * @param RequestInterface $request Request to prepare - * @throws \Exception on error preparing the request - */ - protected function beforeSend(RequestInterface $request) - { - try { - $state = $request->setState(RequestInterface::STATE_TRANSFER); - if ($state == RequestInterface::STATE_TRANSFER) { - // Add the request curl handle to the multi handle - $handle = $this->createCurlHandle($request)->getHandle(); - $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $handle)); - } else { - // Requests might decide they don't need to be sent just before transfer (e.g. CachePlugin) - $this->remove($request); - if ($state == RequestInterface::STATE_COMPLETE) { - $this->successful[] = $request; - } - } - } catch (\Exception $e) { - // Queue the exception to be thrown when sent - $this->removeErroredRequest($request, $e); - } - } - - /** - * Create a curl handle for a request - * - * @param RequestInterface $request Request - * - * @return CurlHandle - */ - protected function createCurlHandle(RequestInterface $request) - { - $wrapper = CurlHandle::factory($request); - $this->handles[$request] = $wrapper; - $this->resourceHash[(int) $wrapper->getHandle()] = $request; - - return $wrapper; - } - - /** - * Get the data from the multi handle - */ - protected function perform() - { - $event = new Event(array('curl_multi' => $this)); - - while ($this->requests) { - // Notify each request as polling - $blocking = $total = 0; - foreach ($this->requests as $request) { - ++$total; - $event['request'] = $request; - $request->getEventDispatcher()->dispatch(self::POLLING_REQUEST, $event); - // The blocking variable just has to be non-falsey to block the loop - if ($request->getParams()->hasKey(self::BLOCKING)) { - ++$blocking; - } - } - if ($blocking == $total) { - // Sleep to prevent eating CPU because no requests are actually pending a select call - usleep(500); - } else { - $this->executeHandles(); - } - } - } - - /** - * Execute and select curl handles - */ - private function executeHandles() - { - // The first curl_multi_select often times out no matter what, but is usually required for fast transfers - $selectTimeout = 0.001; - $active = false; - do { - while (($mrc = curl_multi_exec($this->multiHandle, $active)) == CURLM_CALL_MULTI_PERFORM); - $this->checkCurlResult($mrc); - $this->processMessages(); - if ($active && curl_multi_select($this->multiHandle, $selectTimeout) === -1) { - // Perform a usleep if a select returns -1: https://bugs.php.net/bug.php?id=61141 - usleep(150); - } - $selectTimeout = $this->selectTimeout; - } while ($active); - } - - /** - * Process any received curl multi messages - */ - private function processMessages() - { - while ($done = curl_multi_info_read($this->multiHandle)) { - $request = $this->resourceHash[(int) $done['handle']]; - try { - $this->processResponse($request, $this->handles[$request], $done); - $this->successful[] = $request; - } catch (\Exception $e) { - $this->removeErroredRequest($request, $e); - } - } - } - - /** - * Remove a request that encountered an exception - * - * @param RequestInterface $request Request to remove - * @param \Exception $e Exception encountered - */ - protected function removeErroredRequest(RequestInterface $request, \Exception $e = null) - { - $this->exceptions[] = array('request' => $request, 'exception' => $e); - $this->remove($request); - $this->dispatch(self::MULTI_EXCEPTION, array('exception' => $e, 'all_exceptions' => $this->exceptions)); - } - - /** - * Check for errors and fix headers of a request based on a curl response - * - * @param RequestInterface $request Request to process - * @param CurlHandle $handle Curl handle object - * @param array $curl Array returned from curl_multi_info_read - * - * @throws CurlException on Curl error - */ - protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) - { - // Set the transfer stats on the response - $handle->updateRequestFromTransfer($request); - // Check if a cURL exception occurred, and if so, notify things - $curlException = $this->isCurlException($request, $handle, $curl); - - // Always remove completed curl handles. They can be added back again - // via events if needed (e.g. ExponentialBackoffPlugin) - $this->removeHandle($request); - - if (!$curlException) { - $state = $request->setState(RequestInterface::STATE_COMPLETE, array('handle' => $handle)); - // Only remove the request if it wasn't resent as a result of the state change - if ($state != RequestInterface::STATE_TRANSFER) { - $this->remove($request); - } - } else { - // Set the state of the request to an error - $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $curlException)); - // Allow things to ignore the error if possible - if ($state != RequestInterface::STATE_TRANSFER) { - $this->remove($request); - } - // The error was not handled, so fail - if ($state == RequestInterface::STATE_ERROR) { - /** @var CurlException $curlException */ - throw $curlException; - } - } - } - - /** - * Remove a curl handle from the curl multi object - * - * @param RequestInterface $request Request that owns the handle - */ - protected function removeHandle(RequestInterface $request) - { - if (isset($this->handles[$request])) { - $handle = $this->handles[$request]; - curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); - unset($this->handles[$request]); - unset($this->resourceHash[(int) $handle->getHandle()]); - $handle->close(); - } - } - - /** - * Check if a cURL transfer resulted in what should be an exception - * - * @param RequestInterface $request Request to check - * @param CurlHandle $handle Curl handle object - * @param array $curl Array returned from curl_multi_info_read - * - * @return CurlException|bool - */ - private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) - { - if (CURLM_OK == $curl['result'] || CURLM_CALL_MULTI_PERFORM == $curl['result']) { - return false; - } - - $handle->setErrorNo($curl['result']); - $e = new CurlException(sprintf('[curl] %s: %s [url] %s', - $handle->getErrorNo(), $handle->getError(), $handle->getUrl())); - $e->setCurlHandle($handle) - ->setRequest($request) - ->setCurlInfo($handle->getInfo()) - ->setError($handle->getError(), $handle->getErrorNo()); - - return $e; - } - - /** - * Throw an exception for a cURL multi response if needed - * - * @param int $code Curl response code - * @throws CurlException - */ - private function checkCurlResult($code) - { - if ($code != CURLM_OK && $code != CURLM_CALL_MULTI_PERFORM) { - throw new CurlException(isset($this->multiErrors[$code]) - ? "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}" - : 'Unexpected cURL error: ' . $code - ); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php deleted file mode 100644 index 0ead75735..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php +++ /dev/null @@ -1,58 +0,0 @@ -maxHandles = $maxHandles; - $this->selectTimeout = $selectTimeout; - // You can get some weird "Too many open files" errors when sending a large amount of requests in parallel. - // These two statements autoload classes before a system runs out of file descriptors so that you can get back - // valuable error messages if you run out. - class_exists('Guzzle\Http\Message\Response'); - class_exists('Guzzle\Http\Exception\CurlException'); - } - - public function add(RequestInterface $request) - { - $this->queued[] = $request; - - return $this; - } - - public function all() - { - $requests = $this->queued; - foreach ($this->handles as $handle) { - $requests = array_merge($requests, $handle->all()); - } - - return $requests; - } - - public function remove(RequestInterface $request) - { - foreach ($this->queued as $i => $r) { - if ($request === $r) { - unset($this->queued[$i]); - return true; - } - } - - foreach ($this->handles as $handle) { - if ($handle->remove($request)) { - return true; - } - } - - return false; - } - - public function reset($hard = false) - { - $this->queued = array(); - $this->groups = array(); - foreach ($this->handles as $handle) { - $handle->reset(); - } - if ($hard) { - $this->handles = array(); - } - - return $this; - } - - public function send() - { - if ($this->queued) { - $group = $this->getAvailableHandle(); - // Add this handle to a list of handles than is claimed - $this->groups[] = $group; - while ($request = array_shift($this->queued)) { - $group->add($request); - } - try { - $group->send(); - array_pop($this->groups); - $this->cleanupHandles(); - } catch (\Exception $e) { - // Remove the group and cleanup if an exception was encountered and no more requests in group - if (!$group->count()) { - array_pop($this->groups); - $this->cleanupHandles(); - } - throw $e; - } - } - } - - public function count() - { - return count($this->all()); - } - - /** - * Get an existing available CurlMulti handle or create a new one - * - * @return CurlMulti - */ - protected function getAvailableHandle() - { - // Grab a handle that is not claimed - foreach ($this->handles as $h) { - if (!in_array($h, $this->groups, true)) { - return $h; - } - } - - // All are claimed, so create one - $handle = new CurlMulti($this->selectTimeout); - $handle->setEventDispatcher($this->getEventDispatcher()); - $this->handles[] = $handle; - - return $handle; - } - - /** - * Trims down unused CurlMulti handles to limit the number of open connections - */ - protected function cleanupHandles() - { - if ($diff = max(0, count($this->handles) - $this->maxHandles)) { - for ($i = count($this->handles) - 1; $i > 0 && $diff > 0; $i--) { - if (!count($this->handles[$i])) { - unset($this->handles[$i]); - $diff--; - } - } - $this->handles = array_values($this->handles); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php deleted file mode 100644 index c3f99dd25..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/CurlVersion.php +++ /dev/null @@ -1,66 +0,0 @@ -version) { - $this->version = curl_version(); - } - - return $this->version; - } - - /** - * Get a specific type of curl information - * - * @param string $type Version information to retrieve. This value is one of: - * - version_number: cURL 24 bit version number - * - version: cURL version number, as a string - * - ssl_version_number: OpenSSL 24 bit version number - * - ssl_version: OpenSSL version number, as a string - * - libz_version: zlib version number, as a string - * - host: Information about the host where cURL was built - * - features: A bitmask of the CURL_VERSION_XXX constants - * - protocols: An array of protocols names supported by cURL - * - * @return string|float|bool if the $type is found, and false if not found - */ - public function get($type) - { - $version = $this->getAll(); - - return isset($version[$type]) ? $version[$type] : false; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php deleted file mode 100644 index 5d1a0cd87..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php +++ /dev/null @@ -1,147 +0,0 @@ -request = $request; - $this->emitIo = $emitIo; - } - - /** - * Receive a response header from curl - * - * @param resource $curl Curl handle - * @param string $header Received header - * - * @return int - */ - public function receiveResponseHeader($curl, $header) - { - static $normalize = array("\r", "\n"); - $length = strlen($header); - $header = str_replace($normalize, '', $header); - - if (strpos($header, 'HTTP/') === 0) { - - $startLine = explode(' ', $header, 3); - $code = $startLine[1]; - $status = isset($startLine[2]) ? $startLine[2] : ''; - - // Only download the body of the response to the specified response - // body when a successful response is received. - if ($code >= 200 && $code < 300) { - $body = $this->request->getResponseBody(); - } else { - $body = EntityBody::factory(); - } - - $response = new Response($code, null, $body); - $response->setStatus($code, $status); - $this->request->startResponse($response); - - $this->request->dispatch('request.receive.status_line', array( - 'request' => $this, - 'line' => $header, - 'status_code' => $code, - 'reason_phrase' => $status - )); - - } elseif ($pos = strpos($header, ':')) { - $this->request->getResponse()->addHeader( - trim(substr($header, 0, $pos)), - trim(substr($header, $pos + 1)) - ); - } - - return $length; - } - - /** - * Received a progress notification - * - * @param int $downloadSize Total download size - * @param int $downloaded Amount of bytes downloaded - * @param int $uploadSize Total upload size - * @param int $uploaded Amount of bytes uploaded - * @param resource $handle CurlHandle object - */ - public function progress($downloadSize, $downloaded, $uploadSize, $uploaded, $handle = null) - { - $this->request->dispatch('curl.callback.progress', array( - 'request' => $this->request, - 'handle' => $handle, - 'download_size' => $downloadSize, - 'downloaded' => $downloaded, - 'upload_size' => $uploadSize, - 'uploaded' => $uploaded - )); - } - - /** - * Write data to the response body of a request - * - * @param resource $curl Curl handle - * @param string $write Data that was received - * - * @return int - */ - public function writeResponseBody($curl, $write) - { - if ($this->emitIo) { - $this->request->dispatch('curl.callback.write', array( - 'request' => $this->request, - 'write' => $write - )); - } - - if ($response = $this->request->getResponse()) { - return $response->getBody()->write($write); - } else { - // Unexpected data received before response headers - abort transfer - return 0; - } - } - - /** - * Read data from the request body and send it to curl - * - * @param resource $ch Curl handle - * @param resource $fd File descriptor - * @param int $length Amount of data to read - * - * @return string - */ - public function readRequestBody($ch, $fd, $length) - { - if (!($body = $this->request->getBody())) { - return ''; - } - - $read = (string) $body->read($length); - if ($this->emitIo) { - $this->request->dispatch('curl.callback.read', array('request' => $this->request, 'read' => $read)); - } - - return $read; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php deleted file mode 100644 index b60d170f0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBody.php +++ /dev/null @@ -1,201 +0,0 @@ -rewindFunction = $callable; - - return $this; - } - - public function rewind() - { - return $this->rewindFunction ? call_user_func($this->rewindFunction, $this) : parent::rewind(); - } - - /** - * Create a new EntityBody from a string - * - * @param string $string String of data - * - * @return EntityBody - */ - public static function fromString($string) - { - $stream = fopen('php://temp', 'r+'); - if ($string !== '') { - fwrite($stream, $string); - rewind($stream); - } - - return new static($stream); - } - - public function compress($filter = 'zlib.deflate') - { - $result = $this->handleCompression($filter); - $this->contentEncoding = $result ? $filter : false; - - return $result; - } - - public function uncompress($filter = 'zlib.inflate') - { - $offsetStart = 0; - - // When inflating gzipped data, the first 10 bytes must be stripped - // if a gzip header is present - if ($filter == 'zlib.inflate') { - // @codeCoverageIgnoreStart - if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { - return false; - } - // @codeCoverageIgnoreEnd - if (stream_get_contents($this->stream, 3, 0) === "\x1f\x8b\x08") { - $offsetStart = 10; - } - } - - $this->contentEncoding = false; - - return $this->handleCompression($filter, $offsetStart); - } - - public function getContentLength() - { - return $this->getSize(); - } - - public function getContentType() - { - return $this->getUri() ? Mimetypes::getInstance()->fromFilename($this->getUri()) : null; - } - - public function getContentMd5($rawOutput = false, $base64Encode = false) - { - if ($hash = self::getHash($this, 'md5', $rawOutput)) { - return $hash && $base64Encode ? base64_encode($hash) : $hash; - } else { - return false; - } - } - - /** - * Calculate the MD5 hash of an entity body - * - * @param EntityBodyInterface $body Entity body to calculate the hash for - * @param bool $rawOutput Whether or not to use raw output - * @param bool $base64Encode Whether or not to base64 encode raw output (only if raw output is true) - * - * @return bool|string Returns an MD5 string on success or FALSE on failure - * @deprecated This will be deprecated soon - * @codeCoverageIgnore - */ - public static function calculateMd5(EntityBodyInterface $body, $rawOutput = false, $base64Encode = false) - { - Version::warn(__CLASS__ . ' is deprecated. Use getContentMd5()'); - return $body->getContentMd5($rawOutput, $base64Encode); - } - - public function setStreamFilterContentEncoding($streamFilterContentEncoding) - { - $this->contentEncoding = $streamFilterContentEncoding; - - return $this; - } - - public function getContentEncoding() - { - return strtr($this->contentEncoding, array( - 'zlib.deflate' => 'gzip', - 'bzip2.compress' => 'compress' - )) ?: false; - } - - protected function handleCompression($filter, $offsetStart = 0) - { - // @codeCoverageIgnoreStart - if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { - return false; - } - // @codeCoverageIgnoreEnd - - $handle = fopen('php://temp', 'r+'); - $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); - if (!$filter) { - return false; - } - - // Seek to the offset start if possible - $this->seek($offsetStart); - while ($data = fread($this->stream, 8096)) { - fwrite($handle, $data); - } - - fclose($this->stream); - $this->stream = $handle; - stream_filter_remove($filter); - $stat = fstat($this->stream); - $this->size = $stat['size']; - $this->rebuildCache(); - $this->seek(0); - - // Remove any existing rewind function as the underlying stream has been replaced - $this->rewindFunction = null; - - return true; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php deleted file mode 100644 index e640f5785..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/EntityBodyInterface.php +++ /dev/null @@ -1,73 +0,0 @@ -isClientError()) { - $label = 'Client error response'; - $class = __NAMESPACE__ . '\\ClientErrorResponseException'; - } elseif ($response->isServerError()) { - $label = 'Server error response'; - $class = __NAMESPACE__ . '\\ServerErrorResponseException'; - } else { - $label = 'Unsuccessful response'; - $class = __CLASS__; - } - - $message = $label . PHP_EOL . implode(PHP_EOL, array( - '[status code] ' . $response->getStatusCode(), - '[reason phrase] ' . $response->getReasonPhrase(), - '[url] ' . $request->getUrl(), - )); - - $e = new $class($message); - $e->setResponse($response); - $e->setRequest($request); - - return $e; - } - - /** - * Set the response that caused the exception - * - * @param Response $response Response to set - */ - public function setResponse(Response $response) - { - $this->response = $response; - } - - /** - * Get the response that caused the exception - * - * @return Response - */ - public function getResponse() - { - return $this->response; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php deleted file mode 100644 index 04d7ddc05..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php +++ /dev/null @@ -1,8 +0,0 @@ -curlError = $error; - $this->curlErrorNo = $number; - - return $this; - } - - /** - * Set the associated curl handle - * - * @param CurlHandle $handle Curl handle - * - * @return self - */ - public function setCurlHandle(CurlHandle $handle) - { - $this->handle = $handle; - - return $this; - } - - /** - * Get the associated cURL handle - * - * @return CurlHandle|null - */ - public function getCurlHandle() - { - return $this->handle; - } - - /** - * Get the associated cURL error message - * - * @return string|null - */ - public function getError() - { - return $this->curlError; - } - - /** - * Get the associated cURL error number - * - * @return int|null - */ - public function getErrorNo() - { - return $this->curlErrorNo; - } - - /** - * Returns curl information about the transfer - * - * @return array - */ - public function getCurlInfo() - { - return $this->curlInfo; - } - - /** - * Set curl transfer information - * - * @param array $info Array of curl transfer information - * - * @return self - * @link http://php.net/manual/en/function.curl-getinfo.php - */ - public function setCurlInfo(array $info) - { - $this->curlInfo = $info; - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php deleted file mode 100644 index ee87295d3..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/HttpException.php +++ /dev/null @@ -1,10 +0,0 @@ -successfulRequests, $this->failedRequests); - } - - /** - * Add to the array of successful requests - * - * @param RequestInterface $request Successful request - * - * @return self - */ - public function addSuccessfulRequest(RequestInterface $request) - { - $this->successfulRequests[] = $request; - - return $this; - } - - /** - * Add to the array of failed requests - * - * @param RequestInterface $request Failed request - * - * @return self - */ - public function addFailedRequest(RequestInterface $request) - { - $this->failedRequests[] = $request; - - return $this; - } - - /** - * Add to the array of failed requests and associate with exceptions - * - * @param RequestInterface $request Failed request - * @param \Exception $exception Exception to add and associate with - * - * @return self - */ - public function addFailedRequestWithException(RequestInterface $request, \Exception $exception) - { - $this->add($exception) - ->addFailedRequest($request) - ->exceptionForRequest[spl_object_hash($request)] = $exception; - - return $this; - } - - /** - * Get the Exception that caused the given $request to fail - * - * @param RequestInterface $request Failed command - * - * @return \Exception|null - */ - public function getExceptionForFailedRequest(RequestInterface $request) - { - $oid = spl_object_hash($request); - - return isset($this->exceptionForRequest[$oid]) ? $this->exceptionForRequest[$oid] : null; - } - - /** - * Set all of the successful requests - * - * @param array Array of requests - * - * @return self - */ - public function setSuccessfulRequests(array $requests) - { - $this->successfulRequests = $requests; - - return $this; - } - - /** - * Set all of the failed requests - * - * @param array Array of requests - * - * @return self - */ - public function setFailedRequests(array $requests) - { - $this->failedRequests = $requests; - - return $this; - } - - /** - * Get an array of successful requests sent in the multi transfer - * - * @return array - */ - public function getSuccessfulRequests() - { - return $this->successfulRequests; - } - - /** - * Get an array of failed requests sent in the multi transfer - * - * @return array - */ - public function getFailedRequests() - { - return $this->failedRequests; - } - - /** - * Check if the exception object contains a request - * - * @param RequestInterface $request Request to check - * - * @return bool - */ - public function containsRequest(RequestInterface $request) - { - return in_array($request, $this->failedRequests, true) || in_array($request, $this->successfulRequests, true); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php deleted file mode 100644 index 274df2cb1..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php +++ /dev/null @@ -1,39 +0,0 @@ -request = $request; - - return $this; - } - - /** - * Get the request that caused the exception - * - * @return RequestInterface - */ - public function getRequest() - { - return $this->request; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php deleted file mode 100644 index f0f7cfe48..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php +++ /dev/null @@ -1,8 +0,0 @@ -eventDispatcher = $eventDispatcher; - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->eventDispatcher = new EventDispatcher(); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - /** - * {@inheritdoc} - * @codeCoverageIgnore - */ - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } - - public function read($length) - { - $event = array( - 'body' => $this, - 'length' => $length, - 'read' => $this->body->read($length) - ); - $this->dispatch('body.read', $event); - - return $event['read']; - } - - public function write($string) - { - $event = array( - 'body' => $this, - 'write' => $string, - 'result' => $this->body->write($string) - ); - $this->dispatch('body.write', $event); - - return $event['result']; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php deleted file mode 100644 index 0d066ffce..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/AbstractMessage.php +++ /dev/null @@ -1,220 +0,0 @@ -params = new Collection(); - $this->headerFactory = new HeaderFactory(); - $this->headers = new HeaderCollection(); - } - - /** - * Set the header factory to use to create headers - * - * @param HeaderFactoryInterface $factory - * - * @return self - */ - public function setHeaderFactory(HeaderFactoryInterface $factory) - { - $this->headerFactory = $factory; - - return $this; - } - - public function getParams() - { - return $this->params; - } - - public function addHeader($header, $value) - { - if (isset($this->headers[$header])) { - $this->headers[$header]->add($value); - } elseif ($value instanceof HeaderInterface) { - $this->headers[$header] = $value; - } else { - $this->headers[$header] = $this->headerFactory->createHeader($header, $value); - } - - return $this; - } - - public function addHeaders(array $headers) - { - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - - return $this; - } - - public function getHeader($header) - { - return $this->headers[$header]; - } - - public function getHeaders() - { - return $this->headers; - } - - public function getHeaderLines() - { - $headers = array(); - foreach ($this->headers as $value) { - $headers[] = $value->getName() . ': ' . $value; - } - - return $headers; - } - - public function setHeader($header, $value) - { - unset($this->headers[$header]); - $this->addHeader($header, $value); - - return $this; - } - - public function setHeaders(array $headers) - { - $this->headers->clear(); - foreach ($headers as $key => $value) { - $this->addHeader($key, $value); - } - - return $this; - } - - public function hasHeader($header) - { - return isset($this->headers[$header]); - } - - public function removeHeader($header) - { - unset($this->headers[$header]); - - return $this; - } - - /** - * @deprecated Use $message->getHeader()->parseParams() - * @codeCoverageIgnore - */ - public function getTokenizedHeader($header, $token = ';') - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader()->parseParams()'); - if ($this->hasHeader($header)) { - $data = new Collection(); - foreach ($this->getHeader($header)->parseParams() as $values) { - foreach ($values as $key => $value) { - if ($value === '') { - $data->set($data->count(), $key); - } else { - $data->add($key, $value); - } - } - } - return $data; - } - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function setTokenizedHeader($header, $data, $token = ';') - { - Version::warn(__METHOD__ . ' is deprecated.'); - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->getDirective()'); - if (!($header = $this->getHeader('Cache-Control'))) { - return null; - } - - return $header->getDirective($directive); - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function hasCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->hasDirective()'); - if ($header = $this->getHeader('Cache-Control')) { - return $header->hasDirective($directive); - } else { - return false; - } - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function addCacheControlDirective($directive, $value = true) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->addDirective()'); - if (!($header = $this->getHeader('Cache-Control'))) { - $this->addHeader('Cache-Control', ''); - $header = $this->getHeader('Cache-Control'); - } - - $header->addDirective($directive, $value); - - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function removeCacheControlDirective($directive) - { - Version::warn(__METHOD__ . ' is deprecated. Use $message->getHeader(\'Cache-Control\')->removeDirective()'); - if ($header = $this->getHeader('Cache-Control')) { - $header->removeDirective($directive); - } - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php deleted file mode 100644 index 212850a25..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php +++ /dev/null @@ -1,247 +0,0 @@ -postFields = new QueryString(); - parent::__construct($method, $url, $headers); - } - - /** - * @return string - */ - public function __toString() - { - // Only attempt to include the POST data if it's only fields - if (count($this->postFields) && empty($this->postFiles)) { - return parent::__toString() . (string) $this->postFields; - } - - return parent::__toString() . $this->body; - } - - public function setState($state, array $context = array()) - { - parent::setState($state, $context); - if ($state == self::STATE_TRANSFER && !$this->body && !count($this->postFields) && !count($this->postFiles)) { - $this->setHeader('Content-Length', 0)->removeHeader('Transfer-Encoding'); - } - - return $this->state; - } - - public function setBody($body, $contentType = null) - { - $this->body = EntityBody::factory($body); - - // Auto detect the Content-Type from the path of the request if possible - if ($contentType === null && !$this->hasHeader('Content-Type')) { - $contentType = $this->body->getContentType(); - } - - if ($contentType) { - $this->setHeader('Content-Type', $contentType); - } - - // Always add the Expect 100-Continue header if the body cannot be rewound. This helps with redirects. - if (!$this->body->isSeekable() && $this->expectCutoff !== false) { - $this->setHeader('Expect', '100-Continue'); - } - - // Set the Content-Length header if it can be determined - $size = $this->body->getContentLength(); - if ($size !== null && $size !== false) { - $this->setHeader('Content-Length', $size); - if ($size > $this->expectCutoff) { - $this->setHeader('Expect', '100-Continue'); - } - } elseif (!$this->hasHeader('Content-Length')) { - if ('1.1' == $this->protocolVersion) { - $this->setHeader('Transfer-Encoding', 'chunked'); - } else { - throw new RequestException( - 'Cannot determine Content-Length and cannot use chunked Transfer-Encoding when using HTTP/1.0' - ); - } - } - - return $this; - } - - public function getBody() - { - return $this->body; - } - - /** - * Set the size that the entity body of the request must exceed before adding the Expect: 100-Continue header. - * - * @param int|bool $size Cutoff in bytes. Set to false to never send the expect header (even with non-seekable data) - * - * @return self - */ - public function setExpectHeaderCutoff($size) - { - $this->expectCutoff = $size; - if ($size === false || !$this->body) { - $this->removeHeader('Expect'); - } elseif ($this->body && $this->body->getSize() && $this->body->getSize() > $size) { - $this->setHeader('Expect', '100-Continue'); - } - - return $this; - } - - public function configureRedirects($strict = false, $maxRedirects = 5) - { - $this->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, $strict); - if ($maxRedirects == 0) { - $this->getParams()->set(RedirectPlugin::DISABLE, true); - } else { - $this->getParams()->set(RedirectPlugin::MAX_REDIRECTS, $maxRedirects); - } - - return $this; - } - - public function getPostField($field) - { - return $this->postFields->get($field); - } - - public function getPostFields() - { - return $this->postFields; - } - - public function setPostField($key, $value) - { - $this->postFields->set($key, $value); - $this->processPostFields(); - - return $this; - } - - public function addPostFields($fields) - { - $this->postFields->merge($fields); - $this->processPostFields(); - - return $this; - } - - public function removePostField($field) - { - $this->postFields->remove($field); - $this->processPostFields(); - - return $this; - } - - public function getPostFiles() - { - return $this->postFiles; - } - - public function getPostFile($fieldName) - { - return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; - } - - public function removePostFile($fieldName) - { - unset($this->postFiles[$fieldName]); - $this->processPostFields(); - - return $this; - } - - public function addPostFile($field, $filename = null, $contentType = null, $postname = null) - { - $data = null; - - if ($field instanceof PostFileInterface) { - $data = $field; - } elseif (is_array($filename)) { - // Allow multiple values to be set in a single key - foreach ($filename as $file) { - $this->addPostFile($field, $file, $contentType); - } - return $this; - } elseif (!is_string($filename)) { - throw new RequestException('The path to a file must be a string'); - } elseif (!empty($filename)) { - // Adding an empty file will cause cURL to error out - $data = new PostFile($field, $filename, $contentType, $postname); - } - - if ($data) { - if (!isset($this->postFiles[$data->getFieldName()])) { - $this->postFiles[$data->getFieldName()] = array($data); - } else { - $this->postFiles[$data->getFieldName()][] = $data; - } - $this->processPostFields(); - } - - return $this; - } - - public function addPostFiles(array $files) - { - foreach ($files as $key => $file) { - if ($file instanceof PostFileInterface) { - $this->addPostFile($file, null, null, false); - } elseif (is_string($file)) { - // Convert non-associative array keys into 'file' - if (is_numeric($key)) { - $key = 'file'; - } - $this->addPostFile($key, $file, null, false); - } else { - throw new RequestException('File must be a string or instance of PostFileInterface'); - } - } - - return $this; - } - - /** - * Determine what type of request should be sent based on post fields - */ - protected function processPostFields() - { - if (!$this->postFiles) { - $this->removeHeader('Expect')->setHeader('Content-Type', self::URL_ENCODED); - } else { - $this->setHeader('Content-Type', self::MULTIPART); - if ($this->expectCutoff !== false) { - $this->setHeader('Expect', '100-Continue'); - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php deleted file mode 100644 index 49ad4595d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php +++ /dev/null @@ -1,137 +0,0 @@ - filenames where filename can be a string or PostFileInterface - * - * @return self - */ - public function addPostFiles(array $files); - - /** - * Configure how redirects are handled for the request - * - * @param bool $strict Set to true to follow strict RFC compliance when redirecting POST requests. Most - * browsers with follow a 301-302 redirect for a POST request with a GET request. This is - * the default behavior of Guzzle. Enable strict redirects to redirect these responses - * with a POST rather than a GET request. - * @param int $maxRedirects Specify the maximum number of allowed redirects. Set to 0 to disable redirects. - * - * @return self - */ - public function configureRedirects($strict = false, $maxRedirects = 5); -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php deleted file mode 100644 index 50597b2a6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header.php +++ /dev/null @@ -1,182 +0,0 @@ -header = trim($header); - $this->glue = $glue; - - foreach ((array) $values as $value) { - foreach ((array) $value as $v) { - $this->values[] = $v; - } - } - } - - public function __toString() - { - return implode($this->glue . ' ', $this->toArray()); - } - - public function add($value) - { - $this->values[] = $value; - - return $this; - } - - public function getName() - { - return $this->header; - } - - public function setName($name) - { - $this->header = $name; - - return $this; - } - - public function setGlue($glue) - { - $this->glue = $glue; - - return $this; - } - - public function getGlue() - { - return $this->glue; - } - - /** - * Normalize the header to be a single header with an array of values. - * - * If any values of the header contains the glue string value (e.g. ","), then the value will be exploded into - * multiple entries in the header. - * - * @return self - */ - public function normalize() - { - $values = $this->toArray(); - - for ($i = 0, $total = count($values); $i < $total; $i++) { - if (strpos($values[$i], $this->glue) !== false) { - // Explode on glue when the glue is not inside of a comma - foreach (preg_split('/' . preg_quote($this->glue) . '(?=([^"]*"[^"]*")*[^"]*$)/', $values[$i]) as $v) { - $values[] = trim($v); - } - unset($values[$i]); - } - } - - $this->values = array_values($values); - - return $this; - } - - public function hasValue($searchValue) - { - return in_array($searchValue, $this->toArray()); - } - - public function removeValue($searchValue) - { - $this->values = array_values(array_filter($this->values, function ($value) use ($searchValue) { - return $value != $searchValue; - })); - - return $this; - } - - public function toArray() - { - return $this->values; - } - - public function count() - { - return count($this->toArray()); - } - - public function getIterator() - { - return new \ArrayIterator($this->toArray()); - } - - public function parseParams() - { - $params = $matches = array(); - $callback = array($this, 'trimHeader'); - - // Normalize the header into a single array and iterate over all values - foreach ($this->normalize()->toArray() as $val) { - $part = array(); - foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) { - if (!preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) { - continue; - } - $pieces = array_map($callback, $matches[0]); - $part[$pieces[0]] = isset($pieces[1]) ? $pieces[1] : ''; - } - if ($part) { - $params[] = $part; - } - } - - return $params; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function hasExactHeader($header) - { - Version::warn(__METHOD__ . ' is deprecated'); - return $this->header == $header; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function raw() - { - Version::warn(__METHOD__ . ' is deprecated. Use toArray()'); - return $this->toArray(); - } - - /** - * Trim a header by removing excess spaces and wrapping quotes - * - * @param $str - * - * @return string - */ - protected function trimHeader($str) - { - static $trimmed = "\"' \n\t"; - - return trim($str, $trimmed); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php deleted file mode 100644 index 77789e51f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/CacheControl.php +++ /dev/null @@ -1,121 +0,0 @@ -directives = null; - } - - public function removeValue($searchValue) - { - parent::removeValue($searchValue); - $this->directives = null; - } - - /** - * Check if a specific cache control directive exists - * - * @param string $param Directive to retrieve - * - * @return bool - */ - public function hasDirective($param) - { - $directives = $this->getDirectives(); - - return isset($directives[$param]); - } - - /** - * Get a specific cache control directive - * - * @param string $param Directive to retrieve - * - * @return string|bool|null - */ - public function getDirective($param) - { - $directives = $this->getDirectives(); - - return isset($directives[$param]) ? $directives[$param] : null; - } - - /** - * Add a cache control directive - * - * @param string $param Directive to add - * @param string $value Value to set - * - * @return self - */ - public function addDirective($param, $value) - { - $directives = $this->getDirectives(); - $directives[$param] = $value; - $this->updateFromDirectives($directives); - - return $this; - } - - /** - * Remove a cache control directive by name - * - * @param string $param Directive to remove - * - * @return self - */ - public function removeDirective($param) - { - $directives = $this->getDirectives(); - unset($directives[$param]); - $this->updateFromDirectives($directives); - - return $this; - } - - /** - * Get an associative array of cache control directives - * - * @return array - */ - public function getDirectives() - { - if ($this->directives === null) { - $this->directives = array(); - foreach ($this->parseParams() as $collection) { - foreach ($collection as $key => $value) { - $this->directives[$key] = $value === '' ? true : $value; - } - } - } - - return $this->directives; - } - - /** - * Updates the header value based on the parsed directives - * - * @param array $directives Array of cache control directives - */ - protected function updateFromDirectives(array $directives) - { - $this->directives = $directives; - $this->values = array(); - - foreach ($directives as $key => $value) { - $this->values[] = $value === true ? $key : "{$key}={$value}"; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php deleted file mode 100644 index 8c7f6aefb..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderCollection.php +++ /dev/null @@ -1,108 +0,0 @@ -headers = $headers; - } - - public function __clone() - { - foreach ($this->headers as &$header) { - $header = clone $header; - } - } - - /** - * Clears the header collection - */ - public function clear() - { - $this->headers = array(); - } - - /** - * Set a header on the collection - * - * @param HeaderInterface $header Header to add - * - * @return self - */ - public function add(HeaderInterface $header) - { - $this->headers[strtolower($header->getName())] = $header; - - return $this; - } - - /** - * Get an array of header objects - * - * @return array - */ - public function getAll() - { - return $this->headers; - } - - /** - * Alias of offsetGet - */ - public function get($key) - { - return $this->offsetGet($key); - } - - public function count() - { - return count($this->headers); - } - - public function offsetExists($offset) - { - return isset($this->headers[strtolower($offset)]); - } - - public function offsetGet($offset) - { - $l = strtolower($offset); - - return isset($this->headers[$l]) ? $this->headers[$l] : null; - } - - public function offsetSet($offset, $value) - { - $this->add($value); - } - - public function offsetUnset($offset) - { - unset($this->headers[strtolower($offset)]); - } - - public function getIterator() - { - return new \ArrayIterator($this->headers); - } - - public function toArray() - { - $result = array(); - foreach ($this->headers as $header) { - $result[$header->getName()] = $header->toArray(); - } - - return $result; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php deleted file mode 100644 index 0273be52f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactory.php +++ /dev/null @@ -1,26 +0,0 @@ - 'Guzzle\Http\Message\Header\CacheControl', - 'link' => 'Guzzle\Http\Message\Header\Link', - ); - - public function createHeader($header, $value = null) - { - $lowercase = strtolower($header); - - return isset($this->mapping[$lowercase]) - ? new $this->mapping[$lowercase]($header, $value) - : new Header($header, $value); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php deleted file mode 100644 index 9457cf64a..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/Header/HeaderFactoryInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -", "rel=\"{$rel}\""); - - foreach ($params as $k => $v) { - $values[] = "{$k}=\"{$v}\""; - } - - return $this->add(implode('; ', $values)); - } - - /** - * Check if a specific link exists for a given rel attribute - * - * @param string $rel rel value - * - * @return bool - */ - public function hasLink($rel) - { - return $this->getLink($rel) !== null; - } - - /** - * Get a specific link for a given rel attribute - * - * @param string $rel Rel value - * - * @return array|null - */ - public function getLink($rel) - { - foreach ($this->getLinks() as $link) { - if (isset($link['rel']) && $link['rel'] == $rel) { - return $link; - } - } - - return null; - } - - /** - * Get an associative array of links - * - * For example: - * Link: ; rel=front; type="image/jpeg", ; rel=back; type="image/jpeg" - * - * - * var_export($response->getLinks()); - * array( - * array( - * 'url' => 'http:/.../front.jpeg', - * 'rel' => 'back', - * 'type' => 'image/jpeg', - * ) - * ) - * - * - * @return array - */ - public function getLinks() - { - $links = $this->parseParams(); - - foreach ($links as &$link) { - $key = key($link); - unset($link[$key]); - $link['url'] = trim($key, '<> '); - } - - return $links; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php deleted file mode 100644 index 62bcd4391..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php +++ /dev/null @@ -1,102 +0,0 @@ -fieldName = $fieldName; - $this->setFilename($filename); - $this->postname = $postname ? $postname : basename($filename); - $this->contentType = $contentType ?: $this->guessContentType(); - } - - public function setFieldName($name) - { - $this->fieldName = $name; - - return $this; - } - - public function getFieldName() - { - return $this->fieldName; - } - - public function setFilename($filename) - { - // Remove leading @ symbol - if (strpos($filename, '@') === 0) { - $filename = substr($filename, 1); - } - - if (!is_readable($filename)) { - throw new InvalidArgumentException("Unable to open {$filename} for reading"); - } - - $this->filename = $filename; - - return $this; - } - - public function setPostname($postname) - { - $this->postname = $postname; - - return $this; - } - - public function getFilename() - { - return $this->filename; - } - - public function getPostname() - { - return $this->postname; - } - - public function setContentType($type) - { - $this->contentType = $type; - - return $this; - } - - public function getContentType() - { - return $this->contentType; - } - - public function getCurlValue() - { - // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax - // See: https://wiki.php.net/rfc/curl-file-upload - if (function_exists('curl_file_create')) { - return curl_file_create($this->filename, $this->contentType, $this->postname); - } - - // Use the old style if using an older version of PHP - $value = "@{$this->filename};filename=" . $this->postname; - if ($this->contentType) { - $value .= ';type=' . $this->contentType; - } - - return $value; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getCurlString() - { - Version::warn(__METHOD__ . ' is deprecated. Use getCurlValue()'); - return $this->getCurlValue(); - } - - /** - * Determine the Content-Type of the file - */ - protected function guessContentType() - { - return Mimetypes::getInstance()->fromFilename($this->filename) ?: 'application/octet-stream'; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php deleted file mode 100644 index 7f0779d1e..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php +++ /dev/null @@ -1,83 +0,0 @@ -method = strtoupper($method); - $this->curlOptions = new Collection(); - $this->setUrl($url); - - if ($headers) { - // Special handling for multi-value headers - foreach ($headers as $key => $value) { - // Deal with collisions with Host and Authorization - if ($key == 'host' || $key == 'Host') { - $this->setHeader($key, $value); - } elseif ($value instanceof HeaderInterface) { - $this->addHeader($key, $value); - } else { - foreach ((array) $value as $v) { - $this->addHeader($key, $v); - } - } - } - } - - $this->setState(self::STATE_NEW); - } - - public function __clone() - { - if ($this->eventDispatcher) { - $this->eventDispatcher = clone $this->eventDispatcher; - } - $this->curlOptions = clone $this->curlOptions; - $this->params = clone $this->params; - $this->url = clone $this->url; - $this->response = $this->responseBody = null; - $this->headers = clone $this->headers; - - $this->setState(RequestInterface::STATE_NEW); - $this->dispatch('request.clone', array('request' => $this)); - } - - /** - * Get the HTTP request as a string - * - * @return string - */ - public function __toString() - { - return $this->getRawHeaders() . "\r\n\r\n"; - } - - /** - * Default method that will throw exceptions if an unsuccessful response is received. - * - * @param Event $event Received - * @throws BadResponseException if the response is not successful - */ - public static function onRequestError(Event $event) - { - $e = BadResponseException::factory($event['request'], $event['response']); - $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray()); - throw $e; - } - - public function setClient(ClientInterface $client) - { - $this->client = $client; - - return $this; - } - - public function getClient() - { - return $this->client; - } - - public function getRawHeaders() - { - $protocolVersion = $this->protocolVersion ?: '1.1'; - - return trim($this->method . ' ' . $this->getResource()) . ' ' - . strtoupper(str_replace('https', 'http', $this->url->getScheme())) - . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); - } - - public function setUrl($url) - { - if ($url instanceof Url) { - $this->url = $url; - } else { - $this->url = Url::factory($url); - } - - // Update the port and host header - $this->setPort($this->url->getPort()); - - if ($this->url->getUsername() || $this->url->getPassword()) { - $this->setAuth($this->url->getUsername(), $this->url->getPassword()); - // Remove the auth info from the URL - $this->url->setUsername(null); - $this->url->setPassword(null); - } - - return $this; - } - - public function send() - { - if (!$this->client) { - throw new RuntimeException('A client must be set on the request'); - } - - return $this->client->send($this); - } - - public function getResponse() - { - return $this->response; - } - - public function getQuery($asString = false) - { - return $asString - ? (string) $this->url->getQuery() - : $this->url->getQuery(); - } - - public function getMethod() - { - return $this->method; - } - - public function getScheme() - { - return $this->url->getScheme(); - } - - public function setScheme($scheme) - { - $this->url->setScheme($scheme); - - return $this; - } - - public function getHost() - { - return $this->url->getHost(); - } - - public function setHost($host) - { - $this->url->setHost($host); - $this->setPort($this->url->getPort()); - - return $this; - } - - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - public function setProtocolVersion($protocol) - { - $this->protocolVersion = $protocol; - - return $this; - } - - public function getPath() - { - return '/' . ltrim($this->url->getPath(), '/'); - } - - public function setPath($path) - { - $this->url->setPath($path); - - return $this; - } - - public function getPort() - { - return $this->url->getPort(); - } - - public function setPort($port) - { - $this->url->setPort($port); - - // Include the port in the Host header if it is not the default port for the scheme of the URL - $scheme = $this->url->getScheme(); - if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) { - $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port); - } else { - $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost()); - } - - return $this; - } - - public function getUsername() - { - return $this->username; - } - - public function getPassword() - { - return $this->password; - } - - public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) - { - static $authMap = array( - 'basic' => CURLAUTH_BASIC, - 'digest' => CURLAUTH_DIGEST, - 'ntlm' => CURLAUTH_NTLM, - 'any' => CURLAUTH_ANY - ); - - // If we got false or null, disable authentication - if (!$user) { - $this->password = $this->username = null; - $this->removeHeader('Authorization'); - $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); - return $this; - } - - if (!is_numeric($scheme)) { - $scheme = strtolower($scheme); - if (!isset($authMap[$scheme])) { - throw new InvalidArgumentException($scheme . ' is not a valid authentication type'); - } - $scheme = $authMap[$scheme]; - } - - $this->username = $user; - $this->password = $password; - - // Bypass CURL when using basic auth to promote connection reuse - if ($scheme == CURLAUTH_BASIC) { - $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); - $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); - } else { - $this->getCurlOptions() - ->set(CURLOPT_HTTPAUTH, $scheme) - ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); - } - - return $this; - } - - public function getResource() - { - $resource = $this->getPath(); - if ($query = (string) $this->url->getQuery()) { - $resource .= '?' . $query; - } - - return $resource; - } - - public function getUrl($asObject = false) - { - return $asObject ? clone $this->url : (string) $this->url; - } - - public function getState() - { - return $this->state; - } - - public function setState($state, array $context = array()) - { - $oldState = $this->state; - $this->state = $state; - - switch ($state) { - case self::STATE_NEW: - $this->response = null; - break; - case self::STATE_TRANSFER: - if ($oldState !== $state) { - // Fix Content-Length and Transfer-Encoding collisions - if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) { - $this->removeHeader('Transfer-Encoding'); - } - $this->dispatch('request.before_send', array('request' => $this)); - } - break; - case self::STATE_COMPLETE: - if ($oldState !== $state) { - $this->processResponse($context); - $this->responseBody = null; - } - break; - case self::STATE_ERROR: - if (isset($context['exception'])) { - $this->dispatch('request.exception', array( - 'request' => $this, - 'response' => isset($context['response']) ? $context['response'] : $this->response, - 'exception' => isset($context['exception']) ? $context['exception'] : null - )); - } - } - - return $this->state; - } - - public function getCurlOptions() - { - return $this->curlOptions; - } - - public function startResponse(Response $response) - { - $this->state = self::STATE_TRANSFER; - $response->setEffectiveUrl((string) $this->getUrl()); - $this->response = $response; - - return $this; - } - - public function setResponse(Response $response, $queued = false) - { - $response->setEffectiveUrl((string) $this->url); - - if ($queued) { - $ed = $this->getEventDispatcher(); - $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) { - $e['request']->setResponse($response); - $ed->removeListener('request.before_send', $f); - }, -9999); - } else { - $this->response = $response; - // If a specific response body is specified, then use it instead of the response's body - if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) { - $this->getResponseBody()->write((string) $this->response->getBody()); - } else { - $this->responseBody = $this->response->getBody(); - } - $this->setState(self::STATE_COMPLETE); - } - - return $this; - } - - public function setResponseBody($body) - { - // Attempt to open a file for writing if a string was passed - if (is_string($body)) { - // @codeCoverageIgnoreStart - if (!($body = fopen($body, 'w+'))) { - throw new InvalidArgumentException('Could not open ' . $body . ' for writing'); - } - // @codeCoverageIgnoreEnd - } - - $this->responseBody = EntityBody::factory($body); - - return $this; - } - - public function getResponseBody() - { - if ($this->responseBody === null) { - $this->responseBody = EntityBody::factory()->setCustomData('default', true); - } - - return $this->responseBody; - } - - /** - * Determine if the response body is repeatable (readable + seekable) - * - * @return bool - * @deprecated Use getResponseBody()->isSeekable() - * @codeCoverageIgnore - */ - public function isResponseBodyRepeatable() - { - Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()'); - return !$this->responseBody ? true : $this->responseBody->isRepeatable(); - } - - public function getCookies() - { - if ($cookie = $this->getHeader('Cookie')) { - $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie); - return $data['cookies']; - } - - return array(); - } - - public function getCookie($name) - { - $cookies = $this->getCookies(); - - return isset($cookies[$name]) ? $cookies[$name] : null; - } - - public function addCookie($name, $value) - { - if (!$this->hasHeader('Cookie')) { - $this->setHeader('Cookie', "{$name}={$value}"); - } else { - $this->getHeader('Cookie')->add("{$name}={$value}"); - } - - // Always use semicolons to separate multiple cookie headers - $this->getHeader('Cookie')->setGlue(';'); - - return $this; - } - - public function removeCookie($name) - { - if ($cookie = $this->getHeader('Cookie')) { - foreach ($cookie as $cookieValue) { - if (strpos($cookieValue, $name . '=') === 0) { - $cookie->removeValue($cookieValue); - } - } - } - - return $this; - } - - public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); - - return $this; - } - - public function getEventDispatcher() - { - if (!$this->eventDispatcher) { - $this->setEventDispatcher(new EventDispatcher()); - } - - return $this->eventDispatcher; - } - - public function dispatch($eventName, array $context = array()) - { - $context['request'] = $this; - - return $this->getEventDispatcher()->dispatch($eventName, new Event($context)); - } - - public function addSubscriber(EventSubscriberInterface $subscriber) - { - $this->getEventDispatcher()->addSubscriber($subscriber); - - return $this; - } - - /** - * Get an array containing the request and response for event notifications - * - * @return array - */ - protected function getEventArray() - { - return array( - 'request' => $this, - 'response' => $this->response - ); - } - - /** - * Process a received response - * - * @param array $context Contextual information - * @throws RequestException|BadResponseException on unsuccessful responses - */ - protected function processResponse(array $context = array()) - { - if (!$this->response) { - // If no response, then processResponse shouldn't have been called - $e = new RequestException('Error completing request'); - $e->setRequest($this); - throw $e; - } - - $this->state = self::STATE_COMPLETE; - - // A request was sent, but we don't know if we'll send more or if the final response will be successful - $this->dispatch('request.sent', $this->getEventArray() + $context); - - // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin) - if ($this->state == RequestInterface::STATE_COMPLETE) { - - // The request completed, so the HTTP transaction is complete - $this->dispatch('request.complete', $this->getEventArray()); - - // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by - // modifying the Event object in your listeners or calling setResponse() on the request - if ($this->response->isError()) { - $event = new Event($this->getEventArray()); - $this->getEventDispatcher()->dispatch('request.error', $event); - // Allow events of request.error to quietly change the response - if ($event['response'] !== $this->response) { - $this->response = $event['response']; - } - } - - // If a successful response was received, dispatch an event - if ($this->response->isSuccessful()) { - $this->dispatch('request.success', $this->getEventArray()); - } - } - } - - /** - * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy - * @codeCoverageIgnore - */ - public function canCache() - { - Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.'); - if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) { - $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy(); - return $canCache->canCacheRequest($this); - } else { - return false; - } - } - - /** - * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now) - * @codeCoverageIgnore - */ - public function setIsRedirect($isRedirect) - { - $this->isRedirect = $isRedirect; - - return $this; - } - - /** - * @deprecated Use the history plugin - * @codeCoverageIgnore - */ - public function isRedirect() - { - Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.'); - return $this->isRedirect; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php deleted file mode 100644 index 598a2f697..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php +++ /dev/null @@ -1,359 +0,0 @@ -methods = array_flip(get_class_methods(__CLASS__)); - } - - public function fromMessage($message) - { - $parsed = ParserRegistry::getInstance()->getParser('message')->parseRequest($message); - - if (!$parsed) { - return false; - } - - $request = $this->fromParts($parsed['method'], $parsed['request_url'], - $parsed['headers'], $parsed['body'], $parsed['protocol'], - $parsed['version']); - - // EntityEnclosingRequest adds an "Expect: 100-Continue" header when using a raw request body for PUT or POST - // requests. This factory method should accurately reflect the message, so here we are removing the Expect - // header if one was not supplied in the message. - if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { - $request->removeHeader('Expect'); - } - - return $request; - } - - public function fromParts( - $method, - array $urlParts, - $headers = null, - $body = null, - $protocol = 'HTTP', - $protocolVersion = '1.1' - ) { - return $this->create($method, Url::buildUrl($urlParts), $headers, $body) - ->setProtocolVersion($protocolVersion); - } - - public function create($method, $url, $headers = null, $body = null, array $options = array()) - { - $method = strtoupper($method); - - if ($method == 'GET' || $method == 'HEAD' || $method == 'TRACE' || $method == 'OPTIONS') { - // Handle non-entity-enclosing request methods - $request = new $this->requestClass($method, $url, $headers); - if ($body) { - // The body is where the response body will be stored - $type = gettype($body); - if ($type == 'string' || $type == 'resource' || $type == 'object') { - $request->setResponseBody($body); - } - } - } else { - // Create an entity enclosing request by default - $request = new $this->entityEnclosingRequestClass($method, $url, $headers); - if ($body || $body === '0') { - // Add POST fields and files to an entity enclosing request if an array is used - if (is_array($body) || $body instanceof Collection) { - // Normalize PHP style cURL uploads with a leading '@' symbol - foreach ($body as $key => $value) { - if (is_string($value) && substr($value, 0, 1) == '@') { - $request->addPostFile($key, $value); - unset($body[$key]); - } - } - // Add the fields if they are still present and not all files - $request->addPostFields($body); - } else { - // Add a raw entity body body to the request - $request->setBody($body, (string) $request->getHeader('Content-Type')); - if ((string) $request->getHeader('Transfer-Encoding') == 'chunked') { - $request->removeHeader('Content-Length'); - } - } - } - } - - if ($options) { - $this->applyOptions($request, $options); - } - - return $request; - } - - /** - * Clone a request while changing the method. Emulates the behavior of - * {@see Guzzle\Http\Message\Request::clone}, but can change the HTTP method. - * - * @param RequestInterface $request Request to clone - * @param string $method Method to set - * - * @return RequestInterface - */ - public function cloneRequestWithMethod(RequestInterface $request, $method) - { - // Create the request with the same client if possible - if ($request->getClient()) { - $cloned = $request->getClient()->createRequest($method, $request->getUrl(), $request->getHeaders()); - } else { - $cloned = $this->create($method, $request->getUrl(), $request->getHeaders()); - } - - $cloned->getCurlOptions()->replace($request->getCurlOptions()->toArray()); - $cloned->setEventDispatcher(clone $request->getEventDispatcher()); - // Ensure that that the Content-Length header is not copied if changing to GET or HEAD - if (!($cloned instanceof EntityEnclosingRequestInterface)) { - $cloned->removeHeader('Content-Length'); - } elseif ($request instanceof EntityEnclosingRequestInterface) { - $cloned->setBody($request->getBody()); - } - $cloned->getParams()->replace($request->getParams()->toArray()); - $cloned->dispatch('request.clone', array('request' => $cloned)); - - return $cloned; - } - - public function applyOptions(RequestInterface $request, array $options = array(), $flags = self::OPTIONS_NONE) - { - // Iterate over each key value pair and attempt to apply a config using function visitors - foreach ($options as $key => $value) { - $method = "visit_{$key}"; - if (isset($this->methods[$method])) { - $this->{$method}($request, $value, $flags); - } - } - } - - protected function visit_headers(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('headers value must be an array'); - } - - if ($flags & self::OPTIONS_AS_DEFAULTS) { - // Merge headers in but do not overwrite existing values - foreach ($value as $key => $header) { - if (!$request->hasHeader($key)) { - $request->setHeader($key, $header); - } - } - } else { - $request->addHeaders($value); - } - } - - protected function visit_body(RequestInterface $request, $value, $flags) - { - if ($request instanceof EntityEnclosingRequestInterface) { - $request->setBody($value); - } else { - throw new InvalidArgumentException('Attempting to set a body on a non-entity-enclosing request'); - } - } - - protected function visit_allow_redirects(RequestInterface $request, $value, $flags) - { - if ($value === false) { - $request->getParams()->set(RedirectPlugin::DISABLE, true); - } - } - - protected function visit_auth(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('auth value must be an array'); - } - - $request->setAuth($value[0], isset($value[1]) ? $value[1] : null, isset($value[2]) ? $value[2] : 'basic'); - } - - protected function visit_query(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('query value must be an array'); - } - - if ($flags & self::OPTIONS_AS_DEFAULTS) { - // Merge query string values in but do not overwrite existing values - $query = $request->getQuery(); - $query->overwriteWith(array_diff_key($value, $query->toArray())); - } else { - $request->getQuery()->overwriteWith($value); - } - } - - protected function visit_cookies(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('cookies value must be an array'); - } - - foreach ($value as $name => $v) { - $request->addCookie($name, $v); - } - } - - protected function visit_events(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('events value must be an array'); - } - - foreach ($value as $name => $method) { - if (is_array($method)) { - $request->getEventDispatcher()->addListener($name, $method[0], $method[1]); - } else { - $request->getEventDispatcher()->addListener($name, $method); - } - } - } - - protected function visit_plugins(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('plugins value must be an array'); - } - - foreach ($value as $plugin) { - $request->addSubscriber($plugin); - } - } - - protected function visit_exceptions(RequestInterface $request, $value, $flags) - { - if ($value === false || $value === 0) { - $dispatcher = $request->getEventDispatcher(); - foreach ($dispatcher->getListeners('request.error') as $listener) { - if (is_array($listener) && $listener[0] == 'Guzzle\Http\Message\Request' && $listener[1] = 'onRequestError') { - $dispatcher->removeListener('request.error', $listener); - break; - } - } - } - } - - protected function visit_save_to(RequestInterface $request, $value, $flags) - { - $request->setResponseBody($value); - } - - protected function visit_params(RequestInterface $request, $value, $flags) - { - if (!is_array($value)) { - throw new InvalidArgumentException('params value must be an array'); - } - - $request->getParams()->overwriteWith($value); - } - - protected function visit_timeout(RequestInterface $request, $value, $flags) - { - if (defined('CURLOPT_TIMEOUT_MS')) { - $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, $value * 1000); - } else { - $request->getCurlOptions()->set(CURLOPT_TIMEOUT, $value); - } - } - - protected function visit_connect_timeout(RequestInterface $request, $value, $flags) - { - if (defined('CURLOPT_CONNECTTIMEOUT_MS')) { - $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, $value * 1000); - } else { - $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, $value); - } - } - - protected function visit_debug(RequestInterface $request, $value, $flags) - { - if ($value) { - $request->getCurlOptions()->set(CURLOPT_VERBOSE, true); - } - } - - protected function visit_verify(RequestInterface $request, $value, $flags) - { - $curl = $request->getCurlOptions(); - if ($value === true || is_string($value)) { - $curl[CURLOPT_SSL_VERIFYHOST] = 2; - $curl[CURLOPT_SSL_VERIFYPEER] = true; - if ($value !== true) { - $curl[CURLOPT_CAINFO] = $value; - } - } elseif ($value === false) { - unset($curl[CURLOPT_CAINFO]); - $curl[CURLOPT_SSL_VERIFYHOST] = 0; - $curl[CURLOPT_SSL_VERIFYPEER] = false; - } - } - - protected function visit_proxy(RequestInterface $request, $value, $flags) - { - $request->getCurlOptions()->set(CURLOPT_PROXY, $value, $flags); - } - - protected function visit_cert(RequestInterface $request, $value, $flags) - { - if (is_array($value)) { - $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value[0]); - $request->getCurlOptions()->set(CURLOPT_SSLCERTPASSWD, $value[1]); - } else { - $request->getCurlOptions()->set(CURLOPT_SSLCERT, $value); - } - } - - protected function visit_ssl_key(RequestInterface $request, $value, $flags) - { - if (is_array($value)) { - $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value[0]); - $request->getCurlOptions()->set(CURLOPT_SSLKEYPASSWD, $value[1]); - } else { - $request->getCurlOptions()->set(CURLOPT_SSLKEY, $value); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php deleted file mode 100644 index 6088f10e9..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php +++ /dev/null @@ -1,105 +0,0 @@ - 'Continue', - 101 => 'Switching Protocols', - 102 => 'Processing', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 208 => 'Already Reported', - 226 => 'IM Used', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 308 => 'Permanent Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Reserved for WebDAV advanced collections expired proposal', - 426 => 'Upgrade required', - 428 => 'Precondition Required', - 429 => 'Too Many Requests', - 431 => 'Request Header Fields Too Large', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 506 => 'Variant Also Negotiates (Experimental)', - 507 => 'Insufficient Storage', - 508 => 'Loop Detected', - 510 => 'Not Extended', - 511 => 'Network Authentication Required', - ); - - /** @var EntityBodyInterface The response body */ - protected $body; - - /** @var string The reason phrase of the response (human readable code) */ - protected $reasonPhrase; - - /** @var string The status code of the response */ - protected $statusCode; - - /** @var array Information about the request */ - protected $info = array(); - - /** @var string The effective URL that returned this response */ - protected $effectiveUrl; - - /** @var array Cacheable response codes (see RFC 2616:13.4) */ - protected static $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); - - /** - * Create a new Response based on a raw response message - * - * @param string $message Response message - * - * @return self|bool Returns false on error - */ - public static function fromMessage($message) - { - $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message); - if (!$data) { - return false; - } - - $response = new static($data['code'], $data['headers'], $data['body']); - $response->setProtocol($data['protocol'], $data['version']) - ->setStatus($data['code'], $data['reason_phrase']); - - // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X) - $contentLength = (string) $response->getHeader('Content-Length'); - $actualLength = strlen($data['body']); - if (strlen($data['body']) > 0 && $contentLength != $actualLength) { - $response->setHeader('Content-Length', $actualLength); - } - - return $response; - } - - /** - * Construct the response - * - * @param string $statusCode The response status code (e.g. 200, 404, etc) - * @param ToArrayInterface|array $headers The response headers - * @param string|resource|EntityBodyInterface $body The body of the response - * - * @throws BadResponseException if an invalid response code is given - */ - public function __construct($statusCode, $headers = null, $body = null) - { - parent::__construct(); - $this->setStatus($statusCode); - $this->body = EntityBody::factory($body !== null ? $body : ''); - - if ($headers) { - if (is_array($headers)) { - $this->setHeaders($headers); - } elseif ($headers instanceof ToArrayInterface) { - $this->setHeaders($headers->toArray()); - } else { - throw new BadResponseException('Invalid headers argument received'); - } - } - } - - /** - * @return string - */ - public function __toString() - { - return $this->getMessage(); - } - - public function serialize() - { - return json_encode(array( - 'status' => $this->statusCode, - 'body' => (string) $this->body, - 'headers' => $this->headers->toArray() - )); - } - - public function unserialize($serialize) - { - $data = json_decode($serialize, true); - $this->__construct($data['status'], $data['headers'], $data['body']); - } - - /** - * Get the response entity body - * - * @param bool $asString Set to TRUE to return a string of the body rather than a full body object - * - * @return EntityBodyInterface|string - */ - public function getBody($asString = false) - { - return $asString ? (string) $this->body : $this->body; - } - - /** - * Set the response entity body - * - * @param EntityBodyInterface|string $body Body to set - * - * @return self - */ - public function setBody($body) - { - $this->body = EntityBody::factory($body); - - return $this; - } - - /** - * Set the protocol and protocol version of the response - * - * @param string $protocol Response protocol - * @param string $version Protocol version - * - * @return self - */ - public function setProtocol($protocol, $version) - { - $this->protocol = $protocol; - $this->protocolVersion = $version; - - return $this; - } - - /** - * Get the protocol used for the response (e.g. HTTP) - * - * @return string - */ - public function getProtocol() - { - return $this->protocol; - } - - /** - * Get the HTTP protocol version - * - * @return string - */ - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - /** - * Get a cURL transfer information - * - * @param string $key A single statistic to check - * - * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key - * is set and not found - * @link http://www.php.net/manual/en/function.curl-getinfo.php - */ - public function getInfo($key = null) - { - if ($key === null) { - return $this->info; - } elseif (array_key_exists($key, $this->info)) { - return $this->info[$key]; - } else { - return null; - } - } - - /** - * Set the transfer information - * - * @param array $info Array of cURL transfer stats - * - * @return self - */ - public function setInfo(array $info) - { - $this->info = $info; - - return $this; - } - - /** - * Set the response status - * - * @param int $statusCode Response status code to set - * @param string $reasonPhrase Response reason phrase - * - * @return self - * @throws BadResponseException when an invalid response code is received - */ - public function setStatus($statusCode, $reasonPhrase = '') - { - $this->statusCode = (int) $statusCode; - - if (!$reasonPhrase && isset(self::$statusTexts[$this->statusCode])) { - $this->reasonPhrase = self::$statusTexts[$this->statusCode]; - } else { - $this->reasonPhrase = $reasonPhrase; - } - - return $this; - } - - /** - * Get the response status code - * - * @return integer - */ - public function getStatusCode() - { - return $this->statusCode; - } - - /** - * Get the entire response as a string - * - * @return string - */ - public function getMessage() - { - $message = $this->getRawHeaders(); - - // Only include the body in the message if the size is < 2MB - $size = $this->body->getSize(); - if ($size < 2097152) { - $message .= (string) $this->body; - } - - return $message; - } - - /** - * Get the the raw message headers as a string - * - * @return string - */ - public function getRawHeaders() - { - $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; - $lines = $this->getHeaderLines(); - if (!empty($lines)) { - $headers .= implode("\r\n", $lines) . "\r\n"; - } - - return $headers . "\r\n"; - } - - /** - * Get the response reason phrase- a human readable version of the numeric - * status code - * - * @return string - */ - public function getReasonPhrase() - { - return $this->reasonPhrase; - } - - /** - * Get the Accept-Ranges HTTP header - * - * @return string Returns what partial content range types this server supports. - */ - public function getAcceptRanges() - { - return (string) $this->getHeader('Accept-Ranges'); - } - - /** - * Calculate the age of the response - * - * @return integer - */ - public function calculateAge() - { - $age = $this->getHeader('Age'); - - if ($age === null && $this->getDate()) { - $age = time() - strtotime($this->getDate()); - } - - return $age === null ? null : (int) (string) $age; - } - - /** - * Get the Age HTTP header - * - * @return integer|null Returns the age the object has been in a proxy cache in seconds. - */ - public function getAge() - { - return (string) $this->getHeader('Age'); - } - - /** - * Get the Allow HTTP header - * - * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed. - */ - public function getAllow() - { - return (string) $this->getHeader('Allow'); - } - - /** - * Check if an HTTP method is allowed by checking the Allow response header - * - * @param string $method Method to check - * - * @return bool - */ - public function isMethodAllowed($method) - { - $allow = $this->getHeader('Allow'); - if ($allow) { - foreach (explode(',', $allow) as $allowable) { - if (!strcasecmp(trim($allowable), $method)) { - return true; - } - } - } - - return false; - } - - /** - * Get the Cache-Control HTTP header - * - * @return string - */ - public function getCacheControl() - { - return (string) $this->getHeader('Cache-Control'); - } - - /** - * Get the Connection HTTP header - * - * @return string - */ - public function getConnection() - { - return (string) $this->getHeader('Connection'); - } - - /** - * Get the Content-Encoding HTTP header - * - * @return string|null - */ - public function getContentEncoding() - { - return (string) $this->getHeader('Content-Encoding'); - } - - /** - * Get the Content-Language HTTP header - * - * @return string|null Returns the language the content is in. - */ - public function getContentLanguage() - { - return (string) $this->getHeader('Content-Language'); - } - - /** - * Get the Content-Length HTTP header - * - * @return integer Returns the length of the response body in bytes - */ - public function getContentLength() - { - return (int) (string) $this->getHeader('Content-Length'); - } - - /** - * Get the Content-Location HTTP header - * - * @return string|null Returns an alternate location for the returned data (e.g /index.htm) - */ - public function getContentLocation() - { - return (string) $this->getHeader('Content-Location'); - } - - /** - * Get the Content-Disposition HTTP header - * - * @return string|null Returns the Content-Disposition header - */ - public function getContentDisposition() - { - return (string) $this->getHeader('Content-Disposition'); - } - - /** - * Get the Content-MD5 HTTP header - * - * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response. - */ - public function getContentMd5() - { - return (string) $this->getHeader('Content-MD5'); - } - - /** - * Get the Content-Range HTTP header - * - * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022). - */ - public function getContentRange() - { - return (string) $this->getHeader('Content-Range'); - } - - /** - * Get the Content-Type HTTP header - * - * @return string Returns the mime type of this content. - */ - public function getContentType() - { - return (string) $this->getHeader('Content-Type'); - } - - /** - * Checks if the Content-Type is of a certain type. This is useful if the - * Content-Type header contains charset information and you need to know if - * the Content-Type matches a particular type. - * - * @param string $type Content type to check against - * - * @return bool - */ - public function isContentType($type) - { - return stripos($this->getHeader('Content-Type'), $type) !== false; - } - - /** - * Get the Date HTTP header - * - * @return string|null Returns the date and time that the message was sent. - */ - public function getDate() - { - return (string) $this->getHeader('Date'); - } - - /** - * Get the ETag HTTP header - * - * @return string|null Returns an identifier for a specific version of a resource, often a Message digest. - */ - public function getEtag() - { - return (string) $this->getHeader('ETag'); - } - - /** - * Get the Expires HTTP header - * - * @return string|null Returns the date/time after which the response is considered stale. - */ - public function getExpires() - { - return (string) $this->getHeader('Expires'); - } - - /** - * Get the Last-Modified HTTP header - * - * @return string|null Returns the last modified date for the requested object, in RFC 2822 format - * (e.g. Tue, 15 Nov 1994 12:45:26 GMT) - */ - public function getLastModified() - { - return (string) $this->getHeader('Last-Modified'); - } - - /** - * Get the Location HTTP header - * - * @return string|null Used in redirection, or when a new resource has been created. - */ - public function getLocation() - { - return (string) $this->getHeader('Location'); - } - - /** - * Get the Pragma HTTP header - * - * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along - * the request-response chain. - */ - public function getPragma() - { - return (string) $this->getHeader('Pragma'); - } - - /** - * Get the Proxy-Authenticate HTTP header - * - * @return string|null Authentication to access the proxy (e.g. Basic) - */ - public function getProxyAuthenticate() - { - return (string) $this->getHeader('Proxy-Authenticate'); - } - - /** - * Get the Retry-After HTTP header - * - * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a - * specified period of time. - */ - public function getRetryAfter() - { - return (string) $this->getHeader('Retry-After'); - } - - /** - * Get the Server HTTP header - * - * @return string|null A name for the server - */ - public function getServer() - { - return (string) $this->getHeader('Server'); - } - - /** - * Get the Set-Cookie HTTP header - * - * @return string|null An HTTP cookie. - */ - public function getSetCookie() - { - return (string) $this->getHeader('Set-Cookie'); - } - - /** - * Get the Trailer HTTP header - * - * @return string|null The Trailer general field value indicates that the given set of header fields is present in - * the trailer of a message encoded with chunked transfer-coding. - */ - public function getTrailer() - { - return (string) $this->getHeader('Trailer'); - } - - /** - * Get the Transfer-Encoding HTTP header - * - * @return string|null The form of encoding used to safely transfer the entity to the user - */ - public function getTransferEncoding() - { - return (string) $this->getHeader('Transfer-Encoding'); - } - - /** - * Get the Vary HTTP header - * - * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached - * response can be used rather than requesting a fresh one from the origin server. - */ - public function getVary() - { - return (string) $this->getHeader('Vary'); - } - - /** - * Get the Via HTTP header - * - * @return string|null Informs the client of proxies through which the response was sent. - */ - public function getVia() - { - return (string) $this->getHeader('Via'); - } - - /** - * Get the Warning HTTP header - * - * @return string|null A general warning about possible problems with the entity body - */ - public function getWarning() - { - return (string) $this->getHeader('Warning'); - } - - /** - * Get the WWW-Authenticate HTTP header - * - * @return string|null Indicates the authentication scheme that should be used to access the requested entity - */ - public function getWwwAuthenticate() - { - return (string) $this->getHeader('WWW-Authenticate'); - } - - /** - * Checks if HTTP Status code is a Client Error (4xx) - * - * @return bool - */ - public function isClientError() - { - return $this->statusCode >= 400 && $this->statusCode < 500; - } - - /** - * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) - * - * @return boolean - */ - public function isError() - { - return $this->isClientError() || $this->isServerError(); - } - - /** - * Checks if HTTP Status code is Information (1xx) - * - * @return bool - */ - public function isInformational() - { - return $this->statusCode < 200; - } - - /** - * Checks if HTTP Status code is a Redirect (3xx) - * - * @return bool - */ - public function isRedirect() - { - return $this->statusCode >= 300 && $this->statusCode < 400; - } - - /** - * Checks if HTTP Status code is Server Error (5xx) - * - * @return bool - */ - public function isServerError() - { - return $this->statusCode >= 500 && $this->statusCode < 600; - } - - /** - * Checks if HTTP Status code is Successful (2xx | 304) - * - * @return bool - */ - public function isSuccessful() - { - return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304; - } - - /** - * Check if the response can be cached based on the response headers - * - * @return bool Returns TRUE if the response can be cached or false if not - */ - public function canCache() - { - // Check if the response is cacheable based on the code - if (!in_array((int) $this->getStatusCode(), self::$cacheResponseCodes)) { - return false; - } - - // Make sure a valid body was returned and can be cached - if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) - && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { - return false; - } - - // Never cache no-store resources (this is a private cache, so private - // can be cached) - if ($this->getHeader('Cache-Control') && $this->getHeader('Cache-Control')->hasDirective('no-store')) { - return false; - } - - return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); - } - - /** - * Gets the number of seconds from the current time in which this response is still considered fresh - * - * @return int|null Returns the number of seconds - */ - public function getMaxAge() - { - if ($header = $this->getHeader('Cache-Control')) { - // s-max-age, then max-age, then Expires - if ($age = $header->getDirective('s-maxage')) { - return $age; - } - if ($age = $header->getDirective('max-age')) { - return $age; - } - } - - if ($this->getHeader('Expires')) { - return strtotime($this->getExpires()) - time(); - } - - return null; - } - - /** - * Check if the response is considered fresh. - * - * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the - * response. - * - * @return bool|null - */ - public function isFresh() - { - $fresh = $this->getFreshness(); - - return $fresh === null ? null : $fresh >= 0; - } - - /** - * Check if the response can be validated against the origin server using a conditional GET request. - * - * @return bool - */ - public function canValidate() - { - return $this->getEtag() || $this->getLastModified(); - } - - /** - * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the - * age of the response (max-age - age). - * - * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired. - * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL - * result means that no freshness information is available. - * - * @return int - */ - public function getFreshness() - { - $maxAge = $this->getMaxAge(); - $age = $this->calculateAge(); - - return $maxAge && $age ? ($maxAge - $age) : null; - } - - /** - * Parse the JSON response body and return an array - * - * @return array|string|int|bool|float - * @throws RuntimeException if the response body is not in JSON format - */ - public function json() - { - $data = json_decode((string) $this->body, true); - if (JSON_ERROR_NONE !== json_last_error()) { - throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error()); - } - - return $data === null ? array() : $data; - } - - /** - * Parse the XML response body and return a \SimpleXMLElement. - * - * In order to prevent XXE attacks, this method disables loading external - * entities. If you rely on external entities, then you must parse the - * XML response manually by accessing the response body directly. - * - * @return \SimpleXMLElement - * @throws RuntimeException if the response body is not in XML format - * @link http://websec.io/2012/08/27/Preventing-XXE-in-PHP.html - */ - public function xml() - { - $errorMessage = null; - $internalErrors = libxml_use_internal_errors(true); - $disableEntities = libxml_disable_entity_loader(true); - libxml_clear_errors(); - - try { - $xml = new \SimpleXMLElement((string) $this->body ?: '', LIBXML_NONET); - if ($error = libxml_get_last_error()) { - $errorMessage = $error->message; - } - } catch (\Exception $e) { - $errorMessage = $e->getMessage(); - } - - libxml_clear_errors(); - libxml_use_internal_errors($internalErrors); - libxml_disable_entity_loader($disableEntities); - - if ($errorMessage) { - throw new RuntimeException('Unable to parse response body into XML: ' . $errorMessage); - } - - return $xml; - } - - /** - * Get the redirect count of this response - * - * @return int - */ - public function getRedirectCount() - { - return (int) $this->params->get(RedirectPlugin::REDIRECT_COUNT); - } - - /** - * Set the effective URL that resulted in this response (e.g. the last redirect URL) - * - * @param string $url The effective URL - * - * @return self - */ - public function setEffectiveUrl($url) - { - $this->effectiveUrl = $url; - - return $this; - } - - /** - * Get the effective URL that resulted in this response (e.g. the last redirect URL) - * - * @return string - */ - public function getEffectiveUrl() - { - return $this->effectiveUrl; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getPreviousResponse() - { - Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin.'); - return null; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function setRequest($request) - { - Version::warn(__METHOD__ . ' is deprecated'); - return $this; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function getRequest() - { - Version::warn(__METHOD__ . ' is deprecated'); - return null; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php deleted file mode 100644 index d71586a05..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Mimetypes.php +++ /dev/null @@ -1,962 +0,0 @@ - 'text/vnd.in3d.3dml', - '3g2' => 'video/3gpp2', - '3gp' => 'video/3gpp', - '7z' => 'application/x-7z-compressed', - 'aab' => 'application/x-authorware-bin', - 'aac' => 'audio/x-aac', - 'aam' => 'application/x-authorware-map', - 'aas' => 'application/x-authorware-seg', - 'abw' => 'application/x-abiword', - 'ac' => 'application/pkix-attr-cert', - 'acc' => 'application/vnd.americandynamics.acc', - 'ace' => 'application/x-ace-compressed', - 'acu' => 'application/vnd.acucobol', - 'acutc' => 'application/vnd.acucorp', - 'adp' => 'audio/adpcm', - 'aep' => 'application/vnd.audiograph', - 'afm' => 'application/x-font-type1', - 'afp' => 'application/vnd.ibm.modcap', - 'ahead' => 'application/vnd.ahead.space', - 'ai' => 'application/postscript', - 'aif' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'air' => 'application/vnd.adobe.air-application-installer-package+zip', - 'ait' => 'application/vnd.dvb.ait', - 'ami' => 'application/vnd.amiga.ami', - 'apk' => 'application/vnd.android.package-archive', - 'application' => 'application/x-ms-application', - 'apr' => 'application/vnd.lotus-approach', - 'asa' => 'text/plain', - 'asax' => 'application/octet-stream', - 'asc' => 'application/pgp-signature', - 'ascx' => 'text/plain', - 'asf' => 'video/x-ms-asf', - 'ashx' => 'text/plain', - 'asm' => 'text/x-asm', - 'asmx' => 'text/plain', - 'aso' => 'application/vnd.accpac.simply.aso', - 'asp' => 'text/plain', - 'aspx' => 'text/plain', - 'asx' => 'video/x-ms-asf', - 'atc' => 'application/vnd.acucorp', - 'atom' => 'application/atom+xml', - 'atomcat' => 'application/atomcat+xml', - 'atomsvc' => 'application/atomsvc+xml', - 'atx' => 'application/vnd.antix.game-component', - 'au' => 'audio/basic', - 'avi' => 'video/x-msvideo', - 'aw' => 'application/applixware', - 'axd' => 'text/plain', - 'azf' => 'application/vnd.airzip.filesecure.azf', - 'azs' => 'application/vnd.airzip.filesecure.azs', - 'azw' => 'application/vnd.amazon.ebook', - 'bat' => 'application/x-msdownload', - 'bcpio' => 'application/x-bcpio', - 'bdf' => 'application/x-font-bdf', - 'bdm' => 'application/vnd.syncml.dm+wbxml', - 'bed' => 'application/vnd.realvnc.bed', - 'bh2' => 'application/vnd.fujitsu.oasysprs', - 'bin' => 'application/octet-stream', - 'bmi' => 'application/vnd.bmi', - 'bmp' => 'image/bmp', - 'book' => 'application/vnd.framemaker', - 'box' => 'application/vnd.previewsystems.box', - 'boz' => 'application/x-bzip2', - 'bpk' => 'application/octet-stream', - 'btif' => 'image/prs.btif', - 'bz' => 'application/x-bzip', - 'bz2' => 'application/x-bzip2', - 'c' => 'text/x-c', - 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', - 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', - 'c4d' => 'application/vnd.clonk.c4group', - 'c4f' => 'application/vnd.clonk.c4group', - 'c4g' => 'application/vnd.clonk.c4group', - 'c4p' => 'application/vnd.clonk.c4group', - 'c4u' => 'application/vnd.clonk.c4group', - 'cab' => 'application/vnd.ms-cab-compressed', - 'car' => 'application/vnd.curl.car', - 'cat' => 'application/vnd.ms-pki.seccat', - 'cc' => 'text/x-c', - 'cct' => 'application/x-director', - 'ccxml' => 'application/ccxml+xml', - 'cdbcmsg' => 'application/vnd.contact.cmsg', - 'cdf' => 'application/x-netcdf', - 'cdkey' => 'application/vnd.mediastation.cdkey', - 'cdmia' => 'application/cdmi-capability', - 'cdmic' => 'application/cdmi-container', - 'cdmid' => 'application/cdmi-domain', - 'cdmio' => 'application/cdmi-object', - 'cdmiq' => 'application/cdmi-queue', - 'cdx' => 'chemical/x-cdx', - 'cdxml' => 'application/vnd.chemdraw+xml', - 'cdy' => 'application/vnd.cinderella', - 'cer' => 'application/pkix-cert', - 'cfc' => 'application/x-coldfusion', - 'cfm' => 'application/x-coldfusion', - 'cgm' => 'image/cgm', - 'chat' => 'application/x-chat', - 'chm' => 'application/vnd.ms-htmlhelp', - 'chrt' => 'application/vnd.kde.kchart', - 'cif' => 'chemical/x-cif', - 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', - 'cil' => 'application/vnd.ms-artgalry', - 'cla' => 'application/vnd.claymore', - 'class' => 'application/java-vm', - 'clkk' => 'application/vnd.crick.clicker.keyboard', - 'clkp' => 'application/vnd.crick.clicker.palette', - 'clkt' => 'application/vnd.crick.clicker.template', - 'clkw' => 'application/vnd.crick.clicker.wordbank', - 'clkx' => 'application/vnd.crick.clicker', - 'clp' => 'application/x-msclip', - 'cmc' => 'application/vnd.cosmocaller', - 'cmdf' => 'chemical/x-cmdf', - 'cml' => 'chemical/x-cml', - 'cmp' => 'application/vnd.yellowriver-custom-menu', - 'cmx' => 'image/x-cmx', - 'cod' => 'application/vnd.rim.cod', - 'com' => 'application/x-msdownload', - 'conf' => 'text/plain', - 'cpio' => 'application/x-cpio', - 'cpp' => 'text/x-c', - 'cpt' => 'application/mac-compactpro', - 'crd' => 'application/x-mscardfile', - 'crl' => 'application/pkix-crl', - 'crt' => 'application/x-x509-ca-cert', - 'cryptonote' => 'application/vnd.rig.cryptonote', - 'cs' => 'text/plain', - 'csh' => 'application/x-csh', - 'csml' => 'chemical/x-csml', - 'csp' => 'application/vnd.commonspace', - 'css' => 'text/css', - 'cst' => 'application/x-director', - 'csv' => 'text/csv', - 'cu' => 'application/cu-seeme', - 'curl' => 'text/vnd.curl', - 'cww' => 'application/prs.cww', - 'cxt' => 'application/x-director', - 'cxx' => 'text/x-c', - 'dae' => 'model/vnd.collada+xml', - 'daf' => 'application/vnd.mobius.daf', - 'dataless' => 'application/vnd.fdsn.seed', - 'davmount' => 'application/davmount+xml', - 'dcr' => 'application/x-director', - 'dcurl' => 'text/vnd.curl.dcurl', - 'dd2' => 'application/vnd.oma.dd2+xml', - 'ddd' => 'application/vnd.fujixerox.ddd', - 'deb' => 'application/x-debian-package', - 'def' => 'text/plain', - 'deploy' => 'application/octet-stream', - 'der' => 'application/x-x509-ca-cert', - 'dfac' => 'application/vnd.dreamfactory', - 'dic' => 'text/x-c', - 'dir' => 'application/x-director', - 'dis' => 'application/vnd.mobius.dis', - 'dist' => 'application/octet-stream', - 'distz' => 'application/octet-stream', - 'djv' => 'image/vnd.djvu', - 'djvu' => 'image/vnd.djvu', - 'dll' => 'application/x-msdownload', - 'dmg' => 'application/octet-stream', - 'dms' => 'application/octet-stream', - 'dna' => 'application/vnd.dna', - 'doc' => 'application/msword', - 'docm' => 'application/vnd.ms-word.document.macroenabled.12', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotm' => 'application/vnd.ms-word.template.macroenabled.12', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dp' => 'application/vnd.osgi.dp', - 'dpg' => 'application/vnd.dpgraph', - 'dra' => 'audio/vnd.dra', - 'dsc' => 'text/prs.lines.tag', - 'dssc' => 'application/dssc+der', - 'dtb' => 'application/x-dtbook+xml', - 'dtd' => 'application/xml-dtd', - 'dts' => 'audio/vnd.dts', - 'dtshd' => 'audio/vnd.dts.hd', - 'dump' => 'application/octet-stream', - 'dvi' => 'application/x-dvi', - 'dwf' => 'model/vnd.dwf', - 'dwg' => 'image/vnd.dwg', - 'dxf' => 'image/vnd.dxf', - 'dxp' => 'application/vnd.spotfire.dxp', - 'dxr' => 'application/x-director', - 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', - 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', - 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', - 'ecma' => 'application/ecmascript', - 'edm' => 'application/vnd.novadigm.edm', - 'edx' => 'application/vnd.novadigm.edx', - 'efif' => 'application/vnd.picsel', - 'ei6' => 'application/vnd.pg.osasli', - 'elc' => 'application/octet-stream', - 'eml' => 'message/rfc822', - 'emma' => 'application/emma+xml', - 'eol' => 'audio/vnd.digital-winds', - 'eot' => 'application/vnd.ms-fontobject', - 'eps' => 'application/postscript', - 'epub' => 'application/epub+zip', - 'es3' => 'application/vnd.eszigno3+xml', - 'esf' => 'application/vnd.epson.esf', - 'et3' => 'application/vnd.eszigno3+xml', - 'etx' => 'text/x-setext', - 'exe' => 'application/x-msdownload', - 'exi' => 'application/exi', - 'ext' => 'application/vnd.novadigm.ext', - 'ez' => 'application/andrew-inset', - 'ez2' => 'application/vnd.ezpix-album', - 'ez3' => 'application/vnd.ezpix-package', - 'f' => 'text/x-fortran', - 'f4v' => 'video/x-f4v', - 'f77' => 'text/x-fortran', - 'f90' => 'text/x-fortran', - 'fbs' => 'image/vnd.fastbidsheet', - 'fcs' => 'application/vnd.isac.fcs', - 'fdf' => 'application/vnd.fdf', - 'fe_launch' => 'application/vnd.denovo.fcselayout-link', - 'fg5' => 'application/vnd.fujitsu.oasysgp', - 'fgd' => 'application/x-director', - 'fh' => 'image/x-freehand', - 'fh4' => 'image/x-freehand', - 'fh5' => 'image/x-freehand', - 'fh7' => 'image/x-freehand', - 'fhc' => 'image/x-freehand', - 'fig' => 'application/x-xfig', - 'fli' => 'video/x-fli', - 'flo' => 'application/vnd.micrografx.flo', - 'flv' => 'video/x-flv', - 'flw' => 'application/vnd.kde.kivio', - 'flx' => 'text/vnd.fmi.flexstor', - 'fly' => 'text/vnd.fly', - 'fm' => 'application/vnd.framemaker', - 'fnc' => 'application/vnd.frogans.fnc', - 'for' => 'text/x-fortran', - 'fpx' => 'image/vnd.fpx', - 'frame' => 'application/vnd.framemaker', - 'fsc' => 'application/vnd.fsc.weblaunch', - 'fst' => 'image/vnd.fst', - 'ftc' => 'application/vnd.fluxtime.clip', - 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', - 'fvt' => 'video/vnd.fvt', - 'fxp' => 'application/vnd.adobe.fxp', - 'fxpl' => 'application/vnd.adobe.fxp', - 'fzs' => 'application/vnd.fuzzysheet', - 'g2w' => 'application/vnd.geoplan', - 'g3' => 'image/g3fax', - 'g3w' => 'application/vnd.geospace', - 'gac' => 'application/vnd.groove-account', - 'gdl' => 'model/vnd.gdl', - 'geo' => 'application/vnd.dynageo', - 'gex' => 'application/vnd.geometry-explorer', - 'ggb' => 'application/vnd.geogebra.file', - 'ggt' => 'application/vnd.geogebra.tool', - 'ghf' => 'application/vnd.groove-help', - 'gif' => 'image/gif', - 'gim' => 'application/vnd.groove-identity-message', - 'gmx' => 'application/vnd.gmx', - 'gnumeric' => 'application/x-gnumeric', - 'gph' => 'application/vnd.flographit', - 'gqf' => 'application/vnd.grafeq', - 'gqs' => 'application/vnd.grafeq', - 'gram' => 'application/srgs', - 'gre' => 'application/vnd.geometry-explorer', - 'grv' => 'application/vnd.groove-injector', - 'grxml' => 'application/srgs+xml', - 'gsf' => 'application/x-font-ghostscript', - 'gtar' => 'application/x-gtar', - 'gtm' => 'application/vnd.groove-tool-message', - 'gtw' => 'model/vnd.gtw', - 'gv' => 'text/vnd.graphviz', - 'gxt' => 'application/vnd.geonext', - 'h' => 'text/x-c', - 'h261' => 'video/h261', - 'h263' => 'video/h263', - 'h264' => 'video/h264', - 'hal' => 'application/vnd.hal+xml', - 'hbci' => 'application/vnd.hbci', - 'hdf' => 'application/x-hdf', - 'hh' => 'text/x-c', - 'hlp' => 'application/winhlp', - 'hpgl' => 'application/vnd.hp-hpgl', - 'hpid' => 'application/vnd.hp-hpid', - 'hps' => 'application/vnd.hp-hps', - 'hqx' => 'application/mac-binhex40', - 'hta' => 'application/octet-stream', - 'htc' => 'text/html', - 'htke' => 'application/vnd.kenameaapp', - 'htm' => 'text/html', - 'html' => 'text/html', - 'hvd' => 'application/vnd.yamaha.hv-dic', - 'hvp' => 'application/vnd.yamaha.hv-voice', - 'hvs' => 'application/vnd.yamaha.hv-script', - 'i2g' => 'application/vnd.intergeo', - 'icc' => 'application/vnd.iccprofile', - 'ice' => 'x-conference/x-cooltalk', - 'icm' => 'application/vnd.iccprofile', - 'ico' => 'image/x-icon', - 'ics' => 'text/calendar', - 'ief' => 'image/ief', - 'ifb' => 'text/calendar', - 'ifm' => 'application/vnd.shana.informed.formdata', - 'iges' => 'model/iges', - 'igl' => 'application/vnd.igloader', - 'igm' => 'application/vnd.insors.igm', - 'igs' => 'model/iges', - 'igx' => 'application/vnd.micrografx.igx', - 'iif' => 'application/vnd.shana.informed.interchange', - 'imp' => 'application/vnd.accpac.simply.imp', - 'ims' => 'application/vnd.ms-ims', - 'in' => 'text/plain', - 'ini' => 'text/plain', - 'ipfix' => 'application/ipfix', - 'ipk' => 'application/vnd.shana.informed.package', - 'irm' => 'application/vnd.ibm.rights-management', - 'irp' => 'application/vnd.irepository.package+xml', - 'iso' => 'application/octet-stream', - 'itp' => 'application/vnd.shana.informed.formtemplate', - 'ivp' => 'application/vnd.immervision-ivp', - 'ivu' => 'application/vnd.immervision-ivu', - 'jad' => 'text/vnd.sun.j2me.app-descriptor', - 'jam' => 'application/vnd.jam', - 'jar' => 'application/java-archive', - 'java' => 'text/x-java-source', - 'jisp' => 'application/vnd.jisp', - 'jlt' => 'application/vnd.hp-jlyt', - 'jnlp' => 'application/x-java-jnlp-file', - 'joda' => 'application/vnd.joost.joda-archive', - 'jpe' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpgm' => 'video/jpm', - 'jpgv' => 'video/jpeg', - 'jpm' => 'video/jpm', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'kar' => 'audio/midi', - 'karbon' => 'application/vnd.kde.karbon', - 'kfo' => 'application/vnd.kde.kformula', - 'kia' => 'application/vnd.kidspiration', - 'kml' => 'application/vnd.google-earth.kml+xml', - 'kmz' => 'application/vnd.google-earth.kmz', - 'kne' => 'application/vnd.kinar', - 'knp' => 'application/vnd.kinar', - 'kon' => 'application/vnd.kde.kontour', - 'kpr' => 'application/vnd.kde.kpresenter', - 'kpt' => 'application/vnd.kde.kpresenter', - 'ksp' => 'application/vnd.kde.kspread', - 'ktr' => 'application/vnd.kahootz', - 'ktx' => 'image/ktx', - 'ktz' => 'application/vnd.kahootz', - 'kwd' => 'application/vnd.kde.kword', - 'kwt' => 'application/vnd.kde.kword', - 'lasxml' => 'application/vnd.las.las+xml', - 'latex' => 'application/x-latex', - 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', - 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', - 'les' => 'application/vnd.hhe.lesson-player', - 'lha' => 'application/octet-stream', - 'link66' => 'application/vnd.route66.link66+xml', - 'list' => 'text/plain', - 'list3820' => 'application/vnd.ibm.modcap', - 'listafp' => 'application/vnd.ibm.modcap', - 'log' => 'text/plain', - 'lostxml' => 'application/lost+xml', - 'lrf' => 'application/octet-stream', - 'lrm' => 'application/vnd.ms-lrm', - 'ltf' => 'application/vnd.frogans.ltf', - 'lvp' => 'audio/vnd.lucent.voice', - 'lwp' => 'application/vnd.lotus-wordpro', - 'lzh' => 'application/octet-stream', - 'm13' => 'application/x-msmediaview', - 'm14' => 'application/x-msmediaview', - 'm1v' => 'video/mpeg', - 'm21' => 'application/mp21', - 'm2a' => 'audio/mpeg', - 'm2v' => 'video/mpeg', - 'm3a' => 'audio/mpeg', - 'm3u' => 'audio/x-mpegurl', - 'm3u8' => 'application/vnd.apple.mpegurl', - 'm4a' => 'audio/mp4', - 'm4u' => 'video/vnd.mpegurl', - 'm4v' => 'video/mp4', - 'ma' => 'application/mathematica', - 'mads' => 'application/mads+xml', - 'mag' => 'application/vnd.ecowin.chart', - 'maker' => 'application/vnd.framemaker', - 'man' => 'text/troff', - 'mathml' => 'application/mathml+xml', - 'mb' => 'application/mathematica', - 'mbk' => 'application/vnd.mobius.mbk', - 'mbox' => 'application/mbox', - 'mc1' => 'application/vnd.medcalcdata', - 'mcd' => 'application/vnd.mcd', - 'mcurl' => 'text/vnd.curl.mcurl', - 'mdb' => 'application/x-msaccess', - 'mdi' => 'image/vnd.ms-modi', - 'me' => 'text/troff', - 'mesh' => 'model/mesh', - 'meta4' => 'application/metalink4+xml', - 'mets' => 'application/mets+xml', - 'mfm' => 'application/vnd.mfmp', - 'mgp' => 'application/vnd.osgeo.mapguide.package', - 'mgz' => 'application/vnd.proteus.magazine', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mif' => 'application/vnd.mif', - 'mime' => 'message/rfc822', - 'mj2' => 'video/mj2', - 'mjp2' => 'video/mj2', - 'mlp' => 'application/vnd.dolby.mlp', - 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', - 'mmf' => 'application/vnd.smaf', - 'mmr' => 'image/vnd.fujixerox.edmics-mmr', - 'mny' => 'application/x-msmoney', - 'mobi' => 'application/x-mobipocket-ebook', - 'mods' => 'application/mods+xml', - 'mov' => 'video/quicktime', - 'movie' => 'video/x-sgi-movie', - 'mp2' => 'audio/mpeg', - 'mp21' => 'application/mp21', - 'mp2a' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mp4a' => 'audio/mp4', - 'mp4s' => 'application/mp4', - 'mp4v' => 'video/mp4', - 'mpc' => 'application/vnd.mophun.certificate', - 'mpe' => 'video/mpeg', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpg4' => 'video/mp4', - 'mpga' => 'audio/mpeg', - 'mpkg' => 'application/vnd.apple.installer+xml', - 'mpm' => 'application/vnd.blueice.multipass', - 'mpn' => 'application/vnd.mophun.application', - 'mpp' => 'application/vnd.ms-project', - 'mpt' => 'application/vnd.ms-project', - 'mpy' => 'application/vnd.ibm.minipay', - 'mqy' => 'application/vnd.mobius.mqy', - 'mrc' => 'application/marc', - 'mrcx' => 'application/marcxml+xml', - 'ms' => 'text/troff', - 'mscml' => 'application/mediaservercontrol+xml', - 'mseed' => 'application/vnd.fdsn.mseed', - 'mseq' => 'application/vnd.mseq', - 'msf' => 'application/vnd.epson.msf', - 'msh' => 'model/mesh', - 'msi' => 'application/x-msdownload', - 'msl' => 'application/vnd.mobius.msl', - 'msty' => 'application/vnd.muvee.style', - 'mts' => 'model/vnd.mts', - 'mus' => 'application/vnd.musician', - 'musicxml' => 'application/vnd.recordare.musicxml+xml', - 'mvb' => 'application/x-msmediaview', - 'mwf' => 'application/vnd.mfer', - 'mxf' => 'application/mxf', - 'mxl' => 'application/vnd.recordare.musicxml', - 'mxml' => 'application/xv+xml', - 'mxs' => 'application/vnd.triscape.mxs', - 'mxu' => 'video/vnd.mpegurl', - 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', - 'n3' => 'text/n3', - 'nb' => 'application/mathematica', - 'nbp' => 'application/vnd.wolfram.player', - 'nc' => 'application/x-netcdf', - 'ncx' => 'application/x-dtbncx+xml', - 'ngdat' => 'application/vnd.nokia.n-gage.data', - 'nlu' => 'application/vnd.neurolanguage.nlu', - 'nml' => 'application/vnd.enliven', - 'nnd' => 'application/vnd.noblenet-directory', - 'nns' => 'application/vnd.noblenet-sealer', - 'nnw' => 'application/vnd.noblenet-web', - 'npx' => 'image/vnd.net-fpx', - 'nsf' => 'application/vnd.lotus-notes', - 'oa2' => 'application/vnd.fujitsu.oasys2', - 'oa3' => 'application/vnd.fujitsu.oasys3', - 'oas' => 'application/vnd.fujitsu.oasys', - 'obd' => 'application/x-msbinder', - 'oda' => 'application/oda', - 'odb' => 'application/vnd.oasis.opendocument.database', - 'odc' => 'application/vnd.oasis.opendocument.chart', - 'odf' => 'application/vnd.oasis.opendocument.formula', - 'odft' => 'application/vnd.oasis.opendocument.formula-template', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odi' => 'application/vnd.oasis.opendocument.image', - 'odm' => 'application/vnd.oasis.opendocument.text-master', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'ogx' => 'application/ogg', - 'onepkg' => 'application/onenote', - 'onetmp' => 'application/onenote', - 'onetoc' => 'application/onenote', - 'onetoc2' => 'application/onenote', - 'opf' => 'application/oebps-package+xml', - 'oprc' => 'application/vnd.palm', - 'org' => 'application/vnd.lotus-organizer', - 'osf' => 'application/vnd.yamaha.openscoreformat', - 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', - 'otc' => 'application/vnd.oasis.opendocument.chart-template', - 'otf' => 'application/x-font-otf', - 'otg' => 'application/vnd.oasis.opendocument.graphics-template', - 'oth' => 'application/vnd.oasis.opendocument.text-web', - 'oti' => 'application/vnd.oasis.opendocument.image-template', - 'otp' => 'application/vnd.oasis.opendocument.presentation-template', - 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', - 'ott' => 'application/vnd.oasis.opendocument.text-template', - 'oxt' => 'application/vnd.openofficeorg.extension', - 'p' => 'text/x-pascal', - 'p10' => 'application/pkcs10', - 'p12' => 'application/x-pkcs12', - 'p7b' => 'application/x-pkcs7-certificates', - 'p7c' => 'application/pkcs7-mime', - 'p7m' => 'application/pkcs7-mime', - 'p7r' => 'application/x-pkcs7-certreqresp', - 'p7s' => 'application/pkcs7-signature', - 'p8' => 'application/pkcs8', - 'pas' => 'text/x-pascal', - 'paw' => 'application/vnd.pawaafile', - 'pbd' => 'application/vnd.powerbuilder6', - 'pbm' => 'image/x-portable-bitmap', - 'pcf' => 'application/x-font-pcf', - 'pcl' => 'application/vnd.hp-pcl', - 'pclxl' => 'application/vnd.hp-pclxl', - 'pct' => 'image/x-pict', - 'pcurl' => 'application/vnd.curl.pcurl', - 'pcx' => 'image/x-pcx', - 'pdb' => 'application/vnd.palm', - 'pdf' => 'application/pdf', - 'pfa' => 'application/x-font-type1', - 'pfb' => 'application/x-font-type1', - 'pfm' => 'application/x-font-type1', - 'pfr' => 'application/font-tdpfr', - 'pfx' => 'application/x-pkcs12', - 'pgm' => 'image/x-portable-graymap', - 'pgn' => 'application/x-chess-pgn', - 'pgp' => 'application/pgp-encrypted', - 'php' => 'text/x-php', - 'phps' => 'application/x-httpd-phps', - 'pic' => 'image/x-pict', - 'pkg' => 'application/octet-stream', - 'pki' => 'application/pkixcmp', - 'pkipath' => 'application/pkix-pkipath', - 'plb' => 'application/vnd.3gpp.pic-bw-large', - 'plc' => 'application/vnd.mobius.plc', - 'plf' => 'application/vnd.pocketlearn', - 'pls' => 'application/pls+xml', - 'pml' => 'application/vnd.ctc-posml', - 'png' => 'image/png', - 'pnm' => 'image/x-portable-anymap', - 'portpkg' => 'application/vnd.macports.portpkg', - 'pot' => 'application/vnd.ms-powerpoint', - 'potm' => 'application/vnd.ms-powerpoint.template.macroenabled.12', - 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', - 'ppam' => 'application/vnd.ms-powerpoint.addin.macroenabled.12', - 'ppd' => 'application/vnd.cups-ppd', - 'ppm' => 'image/x-portable-pixmap', - 'pps' => 'application/vnd.ms-powerpoint', - 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroenabled.12', - 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', - 'ppt' => 'application/vnd.ms-powerpoint', - 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroenabled.12', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'pqa' => 'application/vnd.palm', - 'prc' => 'application/x-mobipocket-ebook', - 'pre' => 'application/vnd.lotus-freelance', - 'prf' => 'application/pics-rules', - 'ps' => 'application/postscript', - 'psb' => 'application/vnd.3gpp.pic-bw-small', - 'psd' => 'image/vnd.adobe.photoshop', - 'psf' => 'application/x-font-linux-psf', - 'pskcxml' => 'application/pskc+xml', - 'ptid' => 'application/vnd.pvi.ptid1', - 'pub' => 'application/x-mspublisher', - 'pvb' => 'application/vnd.3gpp.pic-bw-var', - 'pwn' => 'application/vnd.3m.post-it-notes', - 'pya' => 'audio/vnd.ms-playready.media.pya', - 'pyv' => 'video/vnd.ms-playready.media.pyv', - 'qam' => 'application/vnd.epson.quickanime', - 'qbo' => 'application/vnd.intu.qbo', - 'qfx' => 'application/vnd.intu.qfx', - 'qps' => 'application/vnd.publishare-delta-tree', - 'qt' => 'video/quicktime', - 'qwd' => 'application/vnd.quark.quarkxpress', - 'qwt' => 'application/vnd.quark.quarkxpress', - 'qxb' => 'application/vnd.quark.quarkxpress', - 'qxd' => 'application/vnd.quark.quarkxpress', - 'qxl' => 'application/vnd.quark.quarkxpress', - 'qxt' => 'application/vnd.quark.quarkxpress', - 'ra' => 'audio/x-pn-realaudio', - 'ram' => 'audio/x-pn-realaudio', - 'rar' => 'application/x-rar-compressed', - 'ras' => 'image/x-cmu-raster', - 'rb' => 'text/plain', - 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', - 'rdf' => 'application/rdf+xml', - 'rdz' => 'application/vnd.data-vision.rdz', - 'rep' => 'application/vnd.businessobjects', - 'res' => 'application/x-dtbresource+xml', - 'resx' => 'text/xml', - 'rgb' => 'image/x-rgb', - 'rif' => 'application/reginfo+xml', - 'rip' => 'audio/vnd.rip', - 'rl' => 'application/resource-lists+xml', - 'rlc' => 'image/vnd.fujixerox.edmics-rlc', - 'rld' => 'application/resource-lists-diff+xml', - 'rm' => 'application/vnd.rn-realmedia', - 'rmi' => 'audio/midi', - 'rmp' => 'audio/x-pn-realaudio-plugin', - 'rms' => 'application/vnd.jcp.javame.midlet-rms', - 'rnc' => 'application/relax-ng-compact-syntax', - 'roff' => 'text/troff', - 'rp9' => 'application/vnd.cloanto.rp9', - 'rpss' => 'application/vnd.nokia.radio-presets', - 'rpst' => 'application/vnd.nokia.radio-preset', - 'rq' => 'application/sparql-query', - 'rs' => 'application/rls-services+xml', - 'rsd' => 'application/rsd+xml', - 'rss' => 'application/rss+xml', - 'rtf' => 'application/rtf', - 'rtx' => 'text/richtext', - 's' => 'text/x-asm', - 'saf' => 'application/vnd.yamaha.smaf-audio', - 'sbml' => 'application/sbml+xml', - 'sc' => 'application/vnd.ibm.secure-container', - 'scd' => 'application/x-msschedule', - 'scm' => 'application/vnd.lotus-screencam', - 'scq' => 'application/scvp-cv-request', - 'scs' => 'application/scvp-cv-response', - 'scurl' => 'text/vnd.curl.scurl', - 'sda' => 'application/vnd.stardivision.draw', - 'sdc' => 'application/vnd.stardivision.calc', - 'sdd' => 'application/vnd.stardivision.impress', - 'sdkd' => 'application/vnd.solent.sdkm+xml', - 'sdkm' => 'application/vnd.solent.sdkm+xml', - 'sdp' => 'application/sdp', - 'sdw' => 'application/vnd.stardivision.writer', - 'see' => 'application/vnd.seemail', - 'seed' => 'application/vnd.fdsn.seed', - 'sema' => 'application/vnd.sema', - 'semd' => 'application/vnd.semd', - 'semf' => 'application/vnd.semf', - 'ser' => 'application/java-serialized-object', - 'setpay' => 'application/set-payment-initiation', - 'setreg' => 'application/set-registration-initiation', - 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', - 'sfs' => 'application/vnd.spotfire.sfs', - 'sgl' => 'application/vnd.stardivision.writer-global', - 'sgm' => 'text/sgml', - 'sgml' => 'text/sgml', - 'sh' => 'application/x-sh', - 'shar' => 'application/x-shar', - 'shf' => 'application/shf+xml', - 'sig' => 'application/pgp-signature', - 'silo' => 'model/mesh', - 'sis' => 'application/vnd.symbian.install', - 'sisx' => 'application/vnd.symbian.install', - 'sit' => 'application/x-stuffit', - 'sitx' => 'application/x-stuffitx', - 'skd' => 'application/vnd.koan', - 'skm' => 'application/vnd.koan', - 'skp' => 'application/vnd.koan', - 'skt' => 'application/vnd.koan', - 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', - 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', - 'slt' => 'application/vnd.epson.salt', - 'sm' => 'application/vnd.stepmania.stepchart', - 'smf' => 'application/vnd.stardivision.math', - 'smi' => 'application/smil+xml', - 'smil' => 'application/smil+xml', - 'snd' => 'audio/basic', - 'snf' => 'application/x-font-snf', - 'so' => 'application/octet-stream', - 'spc' => 'application/x-pkcs7-certificates', - 'spf' => 'application/vnd.yamaha.smaf-phrase', - 'spl' => 'application/x-futuresplash', - 'spot' => 'text/vnd.in3d.spot', - 'spp' => 'application/scvp-vp-response', - 'spq' => 'application/scvp-vp-request', - 'spx' => 'audio/ogg', - 'src' => 'application/x-wais-source', - 'sru' => 'application/sru+xml', - 'srx' => 'application/sparql-results+xml', - 'sse' => 'application/vnd.kodak-descriptor', - 'ssf' => 'application/vnd.epson.ssf', - 'ssml' => 'application/ssml+xml', - 'st' => 'application/vnd.sailingtracker.track', - 'stc' => 'application/vnd.sun.xml.calc.template', - 'std' => 'application/vnd.sun.xml.draw.template', - 'stf' => 'application/vnd.wt.stf', - 'sti' => 'application/vnd.sun.xml.impress.template', - 'stk' => 'application/hyperstudio', - 'stl' => 'application/vnd.ms-pki.stl', - 'str' => 'application/vnd.pg.format', - 'stw' => 'application/vnd.sun.xml.writer.template', - 'sub' => 'image/vnd.dvb.subtitle', - 'sus' => 'application/vnd.sus-calendar', - 'susp' => 'application/vnd.sus-calendar', - 'sv4cpio' => 'application/x-sv4cpio', - 'sv4crc' => 'application/x-sv4crc', - 'svc' => 'application/vnd.dvb.service', - 'svd' => 'application/vnd.svd', - 'svg' => 'image/svg+xml', - 'svgz' => 'image/svg+xml', - 'swa' => 'application/x-director', - 'swf' => 'application/x-shockwave-flash', - 'swi' => 'application/vnd.aristanetworks.swi', - 'sxc' => 'application/vnd.sun.xml.calc', - 'sxd' => 'application/vnd.sun.xml.draw', - 'sxg' => 'application/vnd.sun.xml.writer.global', - 'sxi' => 'application/vnd.sun.xml.impress', - 'sxm' => 'application/vnd.sun.xml.math', - 'sxw' => 'application/vnd.sun.xml.writer', - 't' => 'text/troff', - 'tao' => 'application/vnd.tao.intent-module-archive', - 'tar' => 'application/x-tar', - 'tcap' => 'application/vnd.3gpp2.tcap', - 'tcl' => 'application/x-tcl', - 'teacher' => 'application/vnd.smart.teacher', - 'tei' => 'application/tei+xml', - 'teicorpus' => 'application/tei+xml', - 'tex' => 'application/x-tex', - 'texi' => 'application/x-texinfo', - 'texinfo' => 'application/x-texinfo', - 'text' => 'text/plain', - 'tfi' => 'application/thraud+xml', - 'tfm' => 'application/x-tex-tfm', - 'thmx' => 'application/vnd.ms-officetheme', - 'tif' => 'image/tiff', - 'tiff' => 'image/tiff', - 'tmo' => 'application/vnd.tmobile-livetv', - 'torrent' => 'application/x-bittorrent', - 'tpl' => 'application/vnd.groove-tool-template', - 'tpt' => 'application/vnd.trid.tpt', - 'tr' => 'text/troff', - 'tra' => 'application/vnd.trueapp', - 'trm' => 'application/x-msterminal', - 'tsd' => 'application/timestamped-data', - 'tsv' => 'text/tab-separated-values', - 'ttc' => 'application/x-font-ttf', - 'ttf' => 'application/x-font-ttf', - 'ttl' => 'text/turtle', - 'twd' => 'application/vnd.simtech-mindmapper', - 'twds' => 'application/vnd.simtech-mindmapper', - 'txd' => 'application/vnd.genomatix.tuxedo', - 'txf' => 'application/vnd.mobius.txf', - 'txt' => 'text/plain', - 'u32' => 'application/x-authorware-bin', - 'udeb' => 'application/x-debian-package', - 'ufd' => 'application/vnd.ufdl', - 'ufdl' => 'application/vnd.ufdl', - 'umj' => 'application/vnd.umajin', - 'unityweb' => 'application/vnd.unity', - 'uoml' => 'application/vnd.uoml+xml', - 'uri' => 'text/uri-list', - 'uris' => 'text/uri-list', - 'urls' => 'text/uri-list', - 'ustar' => 'application/x-ustar', - 'utz' => 'application/vnd.uiq.theme', - 'uu' => 'text/x-uuencode', - 'uva' => 'audio/vnd.dece.audio', - 'uvd' => 'application/vnd.dece.data', - 'uvf' => 'application/vnd.dece.data', - 'uvg' => 'image/vnd.dece.graphic', - 'uvh' => 'video/vnd.dece.hd', - 'uvi' => 'image/vnd.dece.graphic', - 'uvm' => 'video/vnd.dece.mobile', - 'uvp' => 'video/vnd.dece.pd', - 'uvs' => 'video/vnd.dece.sd', - 'uvt' => 'application/vnd.dece.ttml+xml', - 'uvu' => 'video/vnd.uvvu.mp4', - 'uvv' => 'video/vnd.dece.video', - 'uvva' => 'audio/vnd.dece.audio', - 'uvvd' => 'application/vnd.dece.data', - 'uvvf' => 'application/vnd.dece.data', - 'uvvg' => 'image/vnd.dece.graphic', - 'uvvh' => 'video/vnd.dece.hd', - 'uvvi' => 'image/vnd.dece.graphic', - 'uvvm' => 'video/vnd.dece.mobile', - 'uvvp' => 'video/vnd.dece.pd', - 'uvvs' => 'video/vnd.dece.sd', - 'uvvt' => 'application/vnd.dece.ttml+xml', - 'uvvu' => 'video/vnd.uvvu.mp4', - 'uvvv' => 'video/vnd.dece.video', - 'uvvx' => 'application/vnd.dece.unspecified', - 'uvx' => 'application/vnd.dece.unspecified', - 'vcd' => 'application/x-cdlink', - 'vcf' => 'text/x-vcard', - 'vcg' => 'application/vnd.groove-vcard', - 'vcs' => 'text/x-vcalendar', - 'vcx' => 'application/vnd.vcx', - 'vis' => 'application/vnd.visionary', - 'viv' => 'video/vnd.vivo', - 'vor' => 'application/vnd.stardivision.writer', - 'vox' => 'application/x-authorware-bin', - 'vrml' => 'model/vrml', - 'vsd' => 'application/vnd.visio', - 'vsf' => 'application/vnd.vsf', - 'vss' => 'application/vnd.visio', - 'vst' => 'application/vnd.visio', - 'vsw' => 'application/vnd.visio', - 'vtu' => 'model/vnd.vtu', - 'vxml' => 'application/voicexml+xml', - 'w3d' => 'application/x-director', - 'wad' => 'application/x-doom', - 'wav' => 'audio/x-wav', - 'wax' => 'audio/x-ms-wax', - 'wbmp' => 'image/vnd.wap.wbmp', - 'wbs' => 'application/vnd.criticaltools.wbs+xml', - 'wbxml' => 'application/vnd.wap.wbxml', - 'wcm' => 'application/vnd.ms-works', - 'wdb' => 'application/vnd.ms-works', - 'weba' => 'audio/webm', - 'webm' => 'video/webm', - 'webp' => 'image/webp', - 'wg' => 'application/vnd.pmi.widget', - 'wgt' => 'application/widget', - 'wks' => 'application/vnd.ms-works', - 'wm' => 'video/x-ms-wm', - 'wma' => 'audio/x-ms-wma', - 'wmd' => 'application/x-ms-wmd', - 'wmf' => 'application/x-msmetafile', - 'wml' => 'text/vnd.wap.wml', - 'wmlc' => 'application/vnd.wap.wmlc', - 'wmls' => 'text/vnd.wap.wmlscript', - 'wmlsc' => 'application/vnd.wap.wmlscriptc', - 'wmv' => 'video/x-ms-wmv', - 'wmx' => 'video/x-ms-wmx', - 'wmz' => 'application/x-ms-wmz', - 'woff' => 'application/x-font-woff', - 'wpd' => 'application/vnd.wordperfect', - 'wpl' => 'application/vnd.ms-wpl', - 'wps' => 'application/vnd.ms-works', - 'wqd' => 'application/vnd.wqd', - 'wri' => 'application/x-mswrite', - 'wrl' => 'model/vrml', - 'wsdl' => 'application/wsdl+xml', - 'wspolicy' => 'application/wspolicy+xml', - 'wtb' => 'application/vnd.webturbo', - 'wvx' => 'video/x-ms-wvx', - 'x32' => 'application/x-authorware-bin', - 'x3d' => 'application/vnd.hzn-3d-crossword', - 'xap' => 'application/x-silverlight-app', - 'xar' => 'application/vnd.xara', - 'xbap' => 'application/x-ms-xbap', - 'xbd' => 'application/vnd.fujixerox.docuworks.binder', - 'xbm' => 'image/x-xbitmap', - 'xdf' => 'application/xcap-diff+xml', - 'xdm' => 'application/vnd.syncml.dm+xml', - 'xdp' => 'application/vnd.adobe.xdp+xml', - 'xdssc' => 'application/dssc+xml', - 'xdw' => 'application/vnd.fujixerox.docuworks', - 'xenc' => 'application/xenc+xml', - 'xer' => 'application/patch-ops-error+xml', - 'xfdf' => 'application/vnd.adobe.xfdf', - 'xfdl' => 'application/vnd.xfdl', - 'xht' => 'application/xhtml+xml', - 'xhtml' => 'application/xhtml+xml', - 'xhvml' => 'application/xv+xml', - 'xif' => 'image/vnd.xiff', - 'xla' => 'application/vnd.ms-excel', - 'xlam' => 'application/vnd.ms-excel.addin.macroenabled.12', - 'xlc' => 'application/vnd.ms-excel', - 'xlm' => 'application/vnd.ms-excel', - 'xls' => 'application/vnd.ms-excel', - 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroenabled.12', - 'xlsm' => 'application/vnd.ms-excel.sheet.macroenabled.12', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xlt' => 'application/vnd.ms-excel', - 'xltm' => 'application/vnd.ms-excel.template.macroenabled.12', - 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', - 'xlw' => 'application/vnd.ms-excel', - 'xml' => 'application/xml', - 'xo' => 'application/vnd.olpc-sugar', - 'xop' => 'application/xop+xml', - 'xpi' => 'application/x-xpinstall', - 'xpm' => 'image/x-xpixmap', - 'xpr' => 'application/vnd.is-xpr', - 'xps' => 'application/vnd.ms-xpsdocument', - 'xpw' => 'application/vnd.intercon.formnet', - 'xpx' => 'application/vnd.intercon.formnet', - 'xsl' => 'application/xml', - 'xslt' => 'application/xslt+xml', - 'xsm' => 'application/vnd.syncml+xml', - 'xspf' => 'application/xspf+xml', - 'xul' => 'application/vnd.mozilla.xul+xml', - 'xvm' => 'application/xv+xml', - 'xvml' => 'application/xv+xml', - 'xwd' => 'image/x-xwindowdump', - 'xyz' => 'chemical/x-xyz', - 'yaml' => 'text/yaml', - 'yang' => 'application/yang', - 'yin' => 'application/yin+xml', - 'yml' => 'text/yaml', - 'zaz' => 'application/vnd.zzazz.deck+xml', - 'zip' => 'application/zip', - 'zir' => 'application/vnd.zul', - 'zirz' => 'application/vnd.zul', - 'zmm' => 'application/vnd.handheld-entertainment+xml' - ); - - /** - * Get a singleton instance of the class - * - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * Get a mimetype value from a file extension - * - * @param string $extension File extension - * - * @return string|null - * - */ - public function fromExtension($extension) - { - $extension = strtolower($extension); - - return isset($this->mimetypes[$extension]) ? $this->mimetypes[$extension] : null; - } - - /** - * Get a mimetype from a filename - * - * @param string $filename Filename to generate a mimetype from - * - * @return string|null - */ - public function fromFilename($filename) - { - return $this->fromExtension(pathinfo($filename, PATHINFO_EXTENSION)); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php deleted file mode 100644 index 4b4e49d05..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/CommaAggregator.php +++ /dev/null @@ -1,20 +0,0 @@ -isUrlEncoding()) { - return array($query->encodeValue($key) => implode(',', array_map(array($query, 'encodeValue'), $value))); - } else { - return array($key => implode(',', $value)); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php deleted file mode 100644 index 1bf1730e4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/DuplicateAggregator.php +++ /dev/null @@ -1,22 +0,0 @@ -isUrlEncoding()) { - return array($query->encodeValue($key) => array_map(array($query, 'encodeValue'), $value)); - } else { - return array($key => $value); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php deleted file mode 100644 index 133ea2bd9..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/PhpAggregator.php +++ /dev/null @@ -1,27 +0,0 @@ - $v) { - $k = "{$key}[{$k}]"; - if (is_array($v)) { - $ret = array_merge($ret, self::aggregate($k, $v, $query)); - } else { - $ret[$query->encodeValue($k)] = $query->encodeValue($v); - } - } - - return $ret; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php deleted file mode 100644 index 72bee620c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/QueryAggregator/QueryAggregatorInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -add($key, $value); - $foundDuplicates = true; - } elseif ($paramIsPhpStyleArray) { - $q[$key] = array($value); - } else { - $q[$key] = $value; - } - } else { - // Uses false by default to represent keys with no trailing "=" sign. - $q->add($key, false); - } - } - - // Use the duplicate aggregator if duplicates were found and not using PHP style arrays - if ($foundDuplicates && !$foundPhpStyle) { - $q->setAggregator(new DuplicateAggregator()); - } - - return $q; - } - - /** - * Convert the query string parameters to a query string string - * - * @return string - * @throws RuntimeException - */ - public function __toString() - { - if (!$this->data) { - return ''; - } - - $queryList = array(); - foreach ($this->prepareData($this->data) as $name => $value) { - $queryList[] = $this->convertKvp($name, $value); - } - - return implode($this->fieldSeparator, $queryList); - } - - /** - * Get the query string field separator - * - * @return string - */ - public function getFieldSeparator() - { - return $this->fieldSeparator; - } - - /** - * Get the query string value separator - * - * @return string - */ - public function getValueSeparator() - { - return $this->valueSeparator; - } - - /** - * Returns the type of URL encoding used by the query string - * - * One of: false, "RFC 3986", or "application/x-www-form-urlencoded" - * - * @return bool|string - */ - public function getUrlEncoding() - { - return $this->urlEncode; - } - - /** - * Returns true or false if using URL encoding - * - * @return bool - */ - public function isUrlEncoding() - { - return $this->urlEncode !== false; - } - - /** - * Provide a function for combining multi-valued query string parameters into a single or multiple fields - * - * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting - * deeply nested query string variables into a flattened array. - * Pass null to use the default PHP style aggregator. For legacy - * reasons, this function accepts a callable that must accepts a - * $key, $value, and query object. - * @return self - * @see \Guzzle\Http\QueryString::aggregateUsingComma() - */ - public function setAggregator(QueryAggregatorInterface $aggregator = null) - { - // Use the default aggregator if none was set - if (!$aggregator) { - if (!self::$defaultAggregator) { - self::$defaultAggregator = new PhpAggregator(); - } - $aggregator = self::$defaultAggregator; - } - - $this->aggregator = $aggregator; - - return $this; - } - - /** - * Set whether or not field names and values should be rawurlencoded - * - * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or - * form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode) - * @return self - */ - public function useUrlEncoding($encode) - { - $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode; - - return $this; - } - - /** - * Set the query string separator - * - * @param string $separator The query string separator that will separate fields - * - * @return self - */ - public function setFieldSeparator($separator) - { - $this->fieldSeparator = $separator; - - return $this; - } - - /** - * Set the query string value separator - * - * @param string $separator The query string separator that will separate values from fields - * - * @return self - */ - public function setValueSeparator($separator) - { - $this->valueSeparator = $separator; - - return $this; - } - - /** - * Returns an array of url encoded field names and values - * - * @return array - */ - public function urlEncode() - { - return $this->prepareData($this->data); - } - - /** - * URL encodes a value based on the url encoding type of the query string object - * - * @param string $value Value to encode - * - * @return string - */ - public function encodeValue($value) - { - if ($this->urlEncode == self::RFC_3986) { - return rawurlencode($value); - } elseif ($this->urlEncode == self::FORM_URLENCODED) { - return urlencode($value); - } else { - return (string) $value; - } - } - - /** - * Url encode parameter data and convert nested query strings into a flattened hash. - * - * @param array $data The data to encode - * - * @return array Returns an array of encoded values and keys - */ - protected function prepareData(array $data) - { - // If no aggregator is present then set the default - if (!$this->aggregator) { - $this->setAggregator(null); - } - - $temp = array(); - foreach ($data as $key => $value) { - if ($value === false || $value === null) { - // False and null will not include the "=". Use an empty string to include the "=". - $temp[$this->encodeValue($key)] = $value; - } elseif (is_array($value)) { - $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this)); - } else { - $temp[$this->encodeValue($key)] = $this->encodeValue($value); - } - } - - return $temp; - } - - /** - * Converts a key value pair that can contain strings, nulls, false, or arrays - * into a single string. - * - * @param string $name Name of the field - * @param mixed $value Value of the field - * @return string - */ - private function convertKvp($name, $value) - { - if ($value === self::BLANK || $value === null || $value === false) { - return $name; - } elseif (!is_array($value)) { - return $name . $this->valueSeparator . $value; - } - - $result = ''; - foreach ($value as $v) { - $result .= $this->convertKvp($name, $v) . $this->fieldSeparator; - } - - return rtrim($result, $this->fieldSeparator); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php deleted file mode 100644 index a34893a66..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/ReadLimitEntityBody.php +++ /dev/null @@ -1,106 +0,0 @@ -setLimit($limit)->setOffset($offset); - } - - /** - * Returns only a subset of the decorated entity body when cast as a string - * {@inheritdoc} - */ - public function __toString() - { - return substr((string) $this->body, $this->offset, $this->limit) ?: ''; - } - - public function isConsumed() - { - return $this->body->isConsumed() || - ($this->body->ftell() >= $this->offset + $this->limit); - } - - /** - * Returns the Content-Length of the limited subset of data - * {@inheritdoc} - */ - public function getContentLength() - { - $length = $this->body->getContentLength(); - - return $length === false - ? $this->limit - : min($this->limit, min($length, $this->offset + $this->limit) - $this->offset); - } - - /** - * Allow for a bounded seek on the read limited entity body - * {@inheritdoc} - */ - public function seek($offset, $whence = SEEK_SET) - { - return $whence === SEEK_SET - ? $this->body->seek(max($this->offset, min($this->offset + $this->limit, $offset))) - : false; - } - - /** - * Set the offset to start limiting from - * - * @param int $offset Offset to seek to and begin byte limiting from - * - * @return self - */ - public function setOffset($offset) - { - $this->body->seek($offset); - $this->offset = $offset; - - return $this; - } - - /** - * Set the limit of bytes that the decorator allows to be read from the stream - * - * @param int $limit Total number of bytes to allow to be read from the stream - * - * @return self - */ - public function setLimit($limit) - { - $this->limit = $limit; - - return $this; - } - - public function read($length) - { - // Check if the current position is less than the total allowed bytes + original offset - $remaining = ($this->offset + $this->limit) - $this->body->ftell(); - if ($remaining > 0) { - // Only return the amount of requested data, ensuring that the byte limit is not exceeded - return $this->body->read(min($remaining, $length)); - } else { - return false; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php deleted file mode 100644 index 1a824b8b7..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/RedirectPlugin.php +++ /dev/null @@ -1,250 +0,0 @@ - array('onRequestSent', 100), - 'request.clone' => 'cleanupRequest', - 'request.before_send' => 'cleanupRequest' - ); - } - - /** - * Clean up the parameters of a request when it is cloned - * - * @param Event $event Event emitted - */ - public function cleanupRequest(Event $event) - { - $params = $event['request']->getParams(); - unset($params[self::REDIRECT_COUNT]); - unset($params[self::PARENT_REQUEST]); - } - - /** - * Called when a request receives a redirect response - * - * @param Event $event Event emitted - */ - public function onRequestSent(Event $event) - { - $response = $event['response']; - $request = $event['request']; - - // Only act on redirect requests with Location headers - if (!$response || $request->getParams()->get(self::DISABLE)) { - return; - } - - // Trace the original request based on parameter history - $original = $this->getOriginalRequest($request); - - // Terminating condition to set the effective response on the original request - if (!$response->isRedirect() || !$response->hasHeader('Location')) { - if ($request !== $original) { - // This is a terminating redirect response, so set it on the original request - $response->getParams()->set(self::REDIRECT_COUNT, $original->getParams()->get(self::REDIRECT_COUNT)); - $original->setResponse($response); - $response->setEffectiveUrl($request->getUrl()); - } - return; - } - - $this->sendRedirectRequest($original, $request, $response); - } - - /** - * Get the original request that initiated a series of redirects - * - * @param RequestInterface $request Request to get the original request from - * - * @return RequestInterface - */ - protected function getOriginalRequest(RequestInterface $request) - { - $original = $request; - // The number of redirects is held on the original request, so determine which request that is - while ($parent = $original->getParams()->get(self::PARENT_REQUEST)) { - $original = $parent; - } - - return $original; - } - - /** - * Create a redirect request for a specific request object - * - * Takes into account strict RFC compliant redirection (e.g. redirect POST with POST) vs doing what most clients do - * (e.g. redirect POST with GET). - * - * @param RequestInterface $request Request being redirected - * @param RequestInterface $original Original request - * @param int $statusCode Status code of the redirect - * @param string $location Location header of the redirect - * - * @return RequestInterface Returns a new redirect request - * @throws CouldNotRewindStreamException If the body needs to be rewound but cannot - */ - protected function createRedirectRequest( - RequestInterface $request, - $statusCode, - $location, - RequestInterface $original - ) { - $redirectRequest = null; - $strict = $original->getParams()->get(self::STRICT_REDIRECTS); - - // Switch method to GET for 303 redirects. 301 and 302 redirects also switch to GET unless we are forcing RFC - // compliance to emulate what most browsers do. NOTE: IE only switches methods on 301/302 when coming from a POST. - if ($request instanceof EntityEnclosingRequestInterface && ($statusCode == 303 || (!$strict && $statusCode <= 302))) { - $redirectRequest = RequestFactory::getInstance()->cloneRequestWithMethod($request, 'GET'); - } else { - $redirectRequest = clone $request; - } - - $redirectRequest->setIsRedirect(true); - // Always use the same response body when redirecting - $redirectRequest->setResponseBody($request->getResponseBody()); - - $location = Url::factory($location); - // If the location is not absolute, then combine it with the original URL - if (!$location->isAbsolute()) { - $originalUrl = $redirectRequest->getUrl(true); - // Remove query string parameters and just take what is present on the redirect Location header - $originalUrl->getQuery()->clear(); - $location = $originalUrl->combine((string) $location, true); - } - - $redirectRequest->setUrl($location); - - // Add the parent request to the request before it sends (make sure it's before the onRequestClone event too) - $redirectRequest->getEventDispatcher()->addListener( - 'request.before_send', - $func = function ($e) use (&$func, $request, $redirectRequest) { - $redirectRequest->getEventDispatcher()->removeListener('request.before_send', $func); - $e['request']->getParams()->set(RedirectPlugin::PARENT_REQUEST, $request); - } - ); - - // Rewind the entity body of the request if needed - if ($redirectRequest instanceof EntityEnclosingRequestInterface && $redirectRequest->getBody()) { - $body = $redirectRequest->getBody(); - // Only rewind the body if some of it has been read already, and throw an exception if the rewind fails - if ($body->ftell() && !$body->rewind()) { - throw new CouldNotRewindStreamException( - 'Unable to rewind the non-seekable entity body of the request after redirecting. cURL probably ' - . 'sent part of body before the redirect occurred. Try adding acustom rewind function using on the ' - . 'entity body of the request using setRewindFunction().' - ); - } - } - - return $redirectRequest; - } - - /** - * Prepare the request for redirection and enforce the maximum number of allowed redirects per client - * - * @param RequestInterface $original Original request - * @param RequestInterface $request Request to prepare and validate - * @param Response $response The current response - * - * @return RequestInterface - */ - protected function prepareRedirection(RequestInterface $original, RequestInterface $request, Response $response) - { - $params = $original->getParams(); - // This is a new redirect, so increment the redirect counter - $current = $params[self::REDIRECT_COUNT] + 1; - $params[self::REDIRECT_COUNT] = $current; - // Use a provided maximum value or default to a max redirect count of 5 - $max = isset($params[self::MAX_REDIRECTS]) ? $params[self::MAX_REDIRECTS] : $this->defaultMaxRedirects; - - // Throw an exception if the redirect count is exceeded - if ($current > $max) { - $this->throwTooManyRedirectsException($original, $max); - return false; - } else { - // Create a redirect request based on the redirect rules set on the request - return $this->createRedirectRequest( - $request, - $response->getStatusCode(), - trim($response->getLocation()), - $original - ); - } - } - - /** - * Send a redirect request and handle any errors - * - * @param RequestInterface $original The originating request - * @param RequestInterface $request The current request being redirected - * @param Response $response The response of the current request - * - * @throws BadResponseException|\Exception - */ - protected function sendRedirectRequest(RequestInterface $original, RequestInterface $request, Response $response) - { - // Validate and create a redirect request based on the original request and current response - if ($redirectRequest = $this->prepareRedirection($original, $request, $response)) { - try { - $redirectRequest->send(); - } catch (BadResponseException $e) { - $e->getResponse(); - if (!$e->getResponse()) { - throw $e; - } - } - } - } - - /** - * Throw a too many redirects exception for a request - * - * @param RequestInterface $original Request - * @param int $max Max allowed redirects - * - * @throws TooManyRedirectsException when too many redirects have been issued - */ - protected function throwTooManyRedirectsException(RequestInterface $original, $max) - { - $original->getEventDispatcher()->addListener( - 'request.complete', - $func = function ($e) use (&$func, $original, $max) { - $original->getEventDispatcher()->removeListener('request.complete', $func); - $str = "{$max} redirects were issued for this request:\n" . $e['request']->getRawHeaders(); - throw new TooManyRedirectsException($str); - } - ); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem b/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem deleted file mode 100644 index 67f696abc..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Resources/cacert.pem +++ /dev/null @@ -1,3785 +0,0 @@ -## -## ca-bundle.crt -- Bundle of CA Root Certificates -## -## Certificate data from Mozilla as of: Tue Jan 28 09:38:07 2014 -## -## This is a bundle of X.509 certificates of public Certificate Authorities -## (CA). These were automatically extracted from Mozilla's root certificates -## file (certdata.txt). This file can be found in the mozilla source tree: -## http://mxr.mozilla.org/mozilla-release/source/security/nss/lib/ckfw/builtins/certdata.txt?raw=1 -## -## It contains the certificates in PEM format and therefore -## can be directly used with curl / libcurl / php_curl, or with -## an Apache+mod_ssl webserver for SSL client authentication. -## Just configure this file as the SSLCACertificateFile. -## - - -GTE CyberTrust Global Root -========================== ------BEGIN CERTIFICATE----- -MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw9HVEUg -Q29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNvbHV0aW9ucywgSW5jLjEjMCEG -A1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEz -MjM1OTAwWjB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL -Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0 -IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4u -sJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcql -HHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8FLztimQID -AQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMW -M4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OF -NMQkpw0PlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ ------END CERTIFICATE----- - -Thawte Server CA -================ ------BEGIN CERTIFICATE----- -MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT -DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs -dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UE -AxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5j -b20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNV -BAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29u -c3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcG -A1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 -ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl -/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg7 -1CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzAR -MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWDTSEwjsrZqG9J -GubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6eQNuozDJ0uW8NxuOzRAvZim+aKZuZ -GCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc= ------END CERTIFICATE----- - -Thawte Premium Server CA -======================== ------BEGIN CERTIFICATE----- -MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkExFTATBgNVBAgT -DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3Vs -dGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UE -AxMYVGhhd3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZl -ckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYT -AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMU -VGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2 -aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZ -cHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 -aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIh -Udib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/ -qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOBgQAm -SCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUIhfzJATj/Tb7yFkJD57taRvvBxhEf -8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7t -UCemDaYj+bvLpgcUQg== ------END CERTIFICATE----- - -Equifax Secure CA -================= ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE -ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 -MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT -B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB -nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR -fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW -8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG -A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE -CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG -A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS -spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB -Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961 -zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB -BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95 -70+sB3c4 ------END CERTIFICATE----- - -Verisign Class 3 Public Primary Certification Authority -======================================================= ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkGA1UEBhMCVVMx -FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVow -XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 -f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol -hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtMEivPLCYA -TxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59Ah -WM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2Omuf -Tqj/ZA1k ------END CERTIFICATE----- - -Verisign Class 3 Public Primary Certification Authority - G2 -============================================================ ------BEGIN CERTIFICATE----- -MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJBgNVBAYTAlVT -MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz -dCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVT -MRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz -dCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCO -FoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71 -lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQAB -MA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01UbSuvDV1Ai2TT -1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7iF6YM40AIOw7n60RzKprxaZLvcRTD -Oaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpYoJ2daZH9 ------END CERTIFICATE----- - -GlobalSign Root CA -================== ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx -GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds -b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV -BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD -VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa -DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc -THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb -Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP -c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX -gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF -AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj -Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG -j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH -hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC -X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- - -GlobalSign Root CA - R2 -======================= ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4GA1UECxMXR2xv -YmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh -bFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT -aWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln -bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6 -ErPLv4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8eoLrvozp -s6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklqtTleiDTsvHgMCJiEbKjN -S7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzdC9XZzPnqJworc5HGnRusyMvo4KD0L5CL -TfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pazq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6C -ygPCm48CAwEAAaOBnDCBmTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E -FgQUm+IHV2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9i -YWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG3lm0mi3f3BmGLjAN -BgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4GsJ0/WwbgcQ3izDJr86iw8bmEbTUsp -9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu -01yiPqFbQfXf5WRDLenVOavSot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG7 -9G+dwfCMNYxdAfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE----- - -ValiCert Class 1 VA -=================== ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp -b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh -bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIy -MjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 -d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEg -UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 -LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIi -GQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCm -DuJWBQ8YTfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwG -lN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8sogTLDAHkY7FkX -icnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPwnXS3qT6gpf+2SQMT2iLM7XGCK5nP -Orf1LXLI ------END CERTIFICATE----- - -ValiCert Class 2 VA -=================== ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp -b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh -bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw -MTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 -d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIg -UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 -LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVC -CSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7Rf -ZHM047QSv4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZ -SWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZoDJJKPTEjlbV -UjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwCW/POuZ6lcg5Ktz885hZo+L7tdEy8 -W9ViH0Pd ------END CERTIFICATE----- - -RSA Root Certificate 1 -====================== ------BEGIN CERTIFICATE----- -MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRp -b24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs -YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZh -bGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAw -MjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0 -d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMg -UG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0 -LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td -3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89H -BFx1cQqYJJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs -3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0WuPIqpsHEzXcjF -V9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/APhmcGcwTTYJBtYze4D1gCCAPRX5r -on+jjBXu ------END CERTIFICATE----- - -Verisign Class 3 Public Primary Certification Authority - G3 -============================================================ ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy -dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1 -EUGO+i2tKmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUc -cLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+Vk7+qRy+oRpfw -EuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJXwzw3sJ2zq/3avL6QaaiMxTJ5Xpj -055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA -ERSWwauSCPc/L8my/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f -j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC -/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565pF4ErWjfJXir0 -xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDa -t20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== ------END CERTIFICATE----- - -Verisign Class 4 Public Primary Certification Authority - G3 -============================================================ ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy -dXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaS -tBO3IFsJ+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM -8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdLMEYH5IBtptiW -Lugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XYufTsgsbSPZUd5cBPhMnZo0QoBmrX -Razwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA -j/ola09b5KROJ1WrIhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt -mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm -fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c2NU8Qh0XwRJd -RTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtG -UPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg== ------END CERTIFICATE----- - -Entrust.net Secure Server CA -============================ ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMCVVMxFDASBgNV -BAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkg -cmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRl -ZDE6MDgGA1UEAxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhv -cml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIG -A1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBi -eSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1p -dGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQ -aO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5 -gXpa0zf3wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcw -ggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHYpIHVMIHSMQsw -CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5l -dC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkw -NTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0Bow -HQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EA -BAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyN -Ewr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9 -n9cd2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- - -Entrust.net Premium 2048 Secure Server CA -========================================= ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChMLRW50cnVzdC5u -ZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxp -bWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNV -BAMTKkVudHJ1c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQx -NzUwNTFaFw0yOTA3MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3 -d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl -MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5u -ZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOL -Gp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSr -hRSGlVuXMlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzW -nLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZdenoVve8AjhUi -VBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH4QIDAQABo0IwQDAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJ -KoZIhvcNAQEFBQADggEBADubj1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPy -T/4xmf3IDExoU8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf -zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5bu/8j72gZyxKT -J1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+bYQLCIt+jerXmCHG8+c8eS9e -nNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/ErfF6adulZkMV8gzURZVE= ------END CERTIFICATE----- - -Baltimore CyberTrust Root -========================= ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJRTESMBAGA1UE -ChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3li -ZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMC -SUUxEjAQBgNVBAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFs -dGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKME -uyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsB -UnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/C -G9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9 -XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjpr -l3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoI -VDaGezq1BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEB -BQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkTI7gQCvlYpNRh -cL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3WgxjkzSswF07r51XgdIGn9w/xZchMB5 -hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsa -Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H -RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- - -Equifax Secure Global eBusiness CA -================================== ------BEGIN CERTIFICATE----- -MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT -RXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNp -bmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMx -HDAaBgNVBAoTE0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEds -b2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRV -PEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzN -qfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxn -hcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j -BBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hs -MA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okEN -I7SS+RkAZ70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIY -NMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV ------END CERTIFICATE----- - -Equifax Secure eBusiness CA 1 -============================= ------BEGIN CERTIFICATE----- -MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT -RXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENB -LTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UE -ChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNz -IENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ -1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4a -IZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBk -MBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4MlIR21kW -Nl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQF -AAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5 -lSE/9dR+WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+ -KpYrtWKmpj29f5JZzVoqgrI3eQ== ------END CERTIFICATE----- - -AddTrust Low-Value Services Root -================================ ------BEGIN CERTIFICATE----- -MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU -cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw -CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO -ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6 -54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr -oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1 -Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui -GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w -HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT -RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw -HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt -ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph -iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY -eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr -mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj -ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= ------END CERTIFICATE----- - -AddTrust External Root -====================== ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYD -VQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEw -NDgzOFowbzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRU -cnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0Eg -Um9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvtH7xsD821 -+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfw -Tz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504B4YCqOmo -aSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy -2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv7 -7+ldU9U0WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0P -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6xCZU7wO94CTL -VBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRk -VHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB -IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl -j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvCNr4TDea9Y355 -e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u -G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- - -AddTrust Public Services Root -============================= ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU -cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ -BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l -dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu -nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i -d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG -Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw -HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G -A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G -A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4 -JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL -+YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao -GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9 -Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H -EufOX1362KqxMy3ZdvJOOjMMK7MtkAY= ------END CERTIFICATE----- - -AddTrust Qualified Certificates Root -==================================== ------BEGIN CERTIFICATE----- -MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU -cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx -CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ -IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx -64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3 -KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o -L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR -wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU -MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE -BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y -azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD -ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG -GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X -dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze -RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB -iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE= ------END CERTIFICATE----- - -Entrust Root Certification Authority -==================================== ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV -BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw -b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG -A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 -MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu -MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu -Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v -dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz -A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww -Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 -j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN -rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw -DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 -MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH -hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM -Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa -v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS -W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 -tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- - -RSA Security 2048 v3 -==================== ------BEGIN CERTIFICATE----- -MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK -ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy -MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb -BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7 -Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb -WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH -KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP -+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/ -MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E -FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY -v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj -0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj -VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395 -nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA -pKnXwiJPZ9d37CAFYd4= ------END CERTIFICATE----- - -GeoTrust Global CA -================== ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVTMRYwFAYDVQQK -Ew1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQw -MDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j -LjEbMBkGA1UEAxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjo -BbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet -8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+Vc -T4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagU -vTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVk -DBF9qn1luMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Q -zxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWVYrmm3ok9Nns4 -d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcFPseKUgzbFbS9bZvlxrFUaKnjaZC2 -mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6p -XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm -Mw== ------END CERTIFICATE----- - -GeoTrust Global CA 2 -==================== ------BEGIN CERTIFICATE----- -MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw -MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j -LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/ -NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k -LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA -Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b -HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH -K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7 -srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh -ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL -OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC -x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF -H4z1Ir+rzoPz4iIprn2DQKi6bA== ------END CERTIFICATE----- - -GeoTrust Universal CA -===================== ------BEGIN CERTIFICATE----- -MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVyc2FsIENBMB4XDTA0MDMwNDA1 -MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IElu -Yy4xHjAcBgNVBAMTFUdlb1RydXN0IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP -ADCCAgoCggIBAKYVVaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9t -JPi8cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTTQjOgNB0e -RXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFhF7em6fgemdtzbvQKoiFs -7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2vc7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d -8Lsrlh/eezJS/R27tQahsiFepdaVaH/wmZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7V -qnJNk22CDtucvc+081xdVHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3Cga -Rr0BHdCXteGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZf9hB -Z3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfReBi9Fi1jUIxaS5BZu -KGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+nhutxx9z3SxPGWX9f5NAEC7S8O08 -ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0 -XG0D08DYj3rWMB8GA1UdIwQYMBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIB -hjANBgkqhkiG9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc -aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fXIwjhmF7DWgh2 -qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzynANXH/KttgCJwpQzgXQQpAvvL -oJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0zuzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsK -xr2EoyNB3tZ3b4XUhRxQ4K5RirqNPnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxF -KyDuSN/n3QmOGKjaQI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2 -DFKWkoRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9ER/frslK -xfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQtDF4JbAiXfKM9fJP/P6EU -p8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/SfuvmbJxPgWp6ZKy7PtXny3YuxadIwVyQD8vI -P/rmMuGNG2+k5o7Y+SlIis5z/iw= ------END CERTIFICATE----- - -GeoTrust Universal CA 2 -======================= ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN -R2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwHhcNMDQwMzA0 -MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3Qg -SW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUA -A4ICDwAwggIKAoICAQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0 -DE81WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUGFF+3Qs17 -j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdqXbboW0W63MOhBW9Wjo8Q -JqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxLse4YuU6W3Nx2/zu+z18DwPw76L5GG//a -QMJS9/7jOvdqdzXQ2o3rXhhqMcceujwbKNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2 -WP0+GfPtDCapkzj4T8FdIgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP -20gaXT73y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRthAAn -ZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgocQIgfksILAAX/8sgC -SqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4Lt1ZrtmhN79UNdxzMk+MBB4zsslG -8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2 -+/CfXGJx7Tz0RzgQKzAfBgNVHSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8E -BAMCAYYwDQYJKoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z -dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQL1EuxBRa3ugZ -4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgrFg5fNuH8KrUwJM/gYwx7WBr+ -mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSoag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpq -A1Ihn0CoZ1Dy81of398j9tx4TuaYT1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpg -Y+RdM4kX2TGq2tbzGDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiP -pm8m1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJVOCiNUW7d -FGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH6aLcr34YEoP9VhdBLtUp -gn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwXQMAJKOSLakhT2+zNVVXxxvjpoixMptEm -X36vWkzaH6byHCx+rgIW0lbQL1dTR+iS ------END CERTIFICATE----- - -America Online Root Certification Authority 1 -============================================= ------BEGIN CERTIFICATE----- -MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT -QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkG -A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg -T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CG -v2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44z -DyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145LcxVR5lu9Rh -sCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP -8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Z -o/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQB8itEf -GDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkFZu90821fnZmv9ov761KyBZiibyrF -VL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft -3OJvx8Fi8eNy1gTIdGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g -Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds -sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 ------END CERTIFICATE----- - -America Online Root Certification Authority 2 -============================================= ------BEGIN CERTIFICATE----- -MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEcMBoGA1UEChMT -QW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkG -A1UEBhMCVVMxHDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2Eg -T25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC206B89en -fHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8 -f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE18aO6lhO -qKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JN -RvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0 -gBe4lL8BPeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn -6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9W6Wa6897Gqid -FEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZo2C7HK2JNDJiuEMhBnIMoVxtRsX6 -Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnj -B453cMor9H124HhnAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op -aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE -AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmnxPBUlgtk87FY -T15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p -+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXg -JXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//Zoy -zH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgO -ZtMADjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh -1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZZLF0Kjhf -GEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y3WRayhgoPmMEEf0cjQAPuDff -Z4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuP -cX/9XhmgD0uRuMRUvAawRY8mkaKO/qk= ------END CERTIFICATE----- - -Visa eCommerce Root -=================== ------BEGIN CERTIFICATE----- -MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBrMQswCQYDVQQG -EwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2Ug -QXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2 -WhcNMjIwNjI0MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMm -VmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv -bW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfL -F9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8b -RaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81q6UCzyr0 -TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI -/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzs -GHxBvfaLdXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG -MB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOCAQEAX/FBfXxc -CLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcRzCSs00Rsca4BIGsDoo8Ytyk6feUW -YFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pz -zkWKsKZJ/0x9nXGIxHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu -YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt -398znM/jra6O1I7mT1GvFpLgXPYHDw== ------END CERTIFICATE----- - -Certum Root CA -============== ------BEGIN CERTIFICATE----- -MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQK -ExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBDQTAeFw0wMjA2MTExMDQ2Mzla -Fw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBMMRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8u -by4xEjAQBgNVBAMTCUNlcnR1bSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6x -wS7TT3zNJc4YPk/EjG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdL -kKWoePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GIULdtlkIJ -89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapuOb7kky/ZR6By6/qmW6/K -Uz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUgAKpoC6EahQGcxEZjgoi2IrHu/qpGWX7P -NSzVttpd90gzFFS269lvzs2I1qsb2pY7HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq -hkiG9w0BAQUFAAOCAQEAuI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+ -GXYkHAQaTOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTgxSvg -GrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1qCjqTE5s7FCMTY5w/ -0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5xO/fIR/RpbxXyEV6DHpx8Uq79AtoS -qFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs6GAqm4VKQPNriiTsBhYscw== ------END CERTIFICATE----- - -Comodo AAA Services root -======================== ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAw -MFoXDTI4MTIzMTIzNTk1OVowezELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hl -c3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNV -BAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQuaBtDFcCLNSS1UY8y2bmhG -C1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe3M/vg4aijJRPn2jymJBGhCfHdr/jzDUs -i14HZGWCwEiwqJH5YZ92IFCokcdmtet4YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszW -Y19zjNoFmag4qMsXeDZRrOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjH -Ypy+g8cmez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQUoBEK -Iz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wewYDVR0f -BHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNl -cy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2Vz -LmNybDANBgkqhkiG9w0BAQUFAAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm -7l3sAg9g1o1QGE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z -8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsil2D4kF501KKaU73yqWjgom7C -12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- - -Comodo Secure Services root -=========================== ------BEGIN CERTIFICATE----- -MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw -MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu -Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi -BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP -9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc -rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC -oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V -p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E -FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w -gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj -YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm -aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm -4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj -Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL -DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw -pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H -RR3B7Hzs/Sk= ------END CERTIFICATE----- - -Comodo Trusted Services root -============================ ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS -R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg -TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw -MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h -bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw -IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7 -3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y -/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6 -juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS -ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud -DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp -ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl -cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw -uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32 -pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA -BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l -R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O -9y5Xt5hwXsjEeLBi ------END CERTIFICATE----- - -QuoVadis Root CA -================ ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJCTTEZMBcGA1UE -ChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAz -MTkxODMzMzNaFw0yMTAzMTcxODMzMzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRp -cyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQD -EyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Ypli4kVEAkOPcahdxYTMuk -J0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2DrOpm2RgbaIr1VxqYuvXtdj182d6UajtL -F8HVj71lODqV0D1VNk7feVcxKh7YWWVJWCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeL -YzcS19Dsw3sgQUSj7cugF+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWen -AScOospUxbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCCAk4w -PQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVvdmFkaXNvZmZzaG9y -ZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREwggENMIIBCQYJKwYBBAG+WAABMIH7 -MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNlIG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmlj -YXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJs -ZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYIKwYBBQUHAgEW -Fmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3TKbkGGew5Oanwl4Rqy+/fMIGu -BgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rqy+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkw -FwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MS4wLAYDVQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6 -tlCLMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSkfnIYj9lo -fFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf87C9TqnN7Az10buYWnuul -LsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1RcHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2x -gI4JVrmcGmD+XcHXetwReNDWXcG31a0ymQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi -5upZIof4l/UO/erMkqQWxFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi -5nrQNiOKSnQ2+Q== ------END CERTIFICATE----- - -QuoVadis Root CA 2 -================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -QuoVadis Root CA 3 -================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -Security Communication Root CA -============================== ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw -8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM -DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX -5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd -DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 -JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw -DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g -0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a -mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ -s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ -6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi -FL39vmwLAw== ------END CERTIFICATE----- - -Sonera Class 2 Root CA -====================== ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEPMA0GA1UEChMG -U29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAxMDQwNjA3Mjk0MFoXDTIxMDQw -NjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNVBAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJh -IENsYXNzMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3 -/Ei9vX+ALTU74W+oZ6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybT -dXnt5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s3TmVToMG -f+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2EjvOr7nQKV0ba5cTppCD8P -tOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu8nYybieDwnPz3BjotJPqdURrBGAgcVeH -nfO+oJAjPYok4doh28MCAwEAAaMzMDEwDwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITT -XjwwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt -0jSv9zilzqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/3DEI -cbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvDFNr450kkkdAdavph -Oe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6Tk6ezAyNlNzZRZxe7EJQY670XcSx -EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH -llpwrN9M ------END CERTIFICATE----- - -Staat der Nederlanden Root CA -============================= ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE -ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g -Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w -HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh -bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt -vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P -jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca -C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth -vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6 -22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV -HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v -dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN -BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR -EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw -MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y -nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR -iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw== ------END CERTIFICATE----- - -TDC Internet Root CA -==================== ------BEGIN CERTIFICATE----- -MIIEKzCCAxOgAwIBAgIEOsylTDANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJESzEVMBMGA1UE -ChMMVERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTAeFw0wMTA0MDUx -NjMzMTdaFw0yMTA0MDUxNzAzMTdaMEMxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxUREMgSW50ZXJu -ZXQxHTAbBgNVBAsTFFREQyBJbnRlcm5ldCBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAxLhAvJHVYx/XmaCLDEAedLdInUaMArLgJF/wGROnN4NrXceO+YQwzho7+vvOi20j -xsNuZp+Jpd/gQlBn+h9sHvTQBda/ytZO5GhgbEaqHF1j4QeGDmUApy6mcca8uYGoOn0a0vnRrEvL -znWv3Hv6gXPU/Lq9QYjUdLP5Xjg6PEOo0pVOd20TDJ2PeAG3WiAfAzc14izbSysseLlJ28TQx5yc -5IogCSEWVmb/Bexb4/DPqyQkXsN/cHoSxNK1EKC2IeGNeGlVRGn1ypYcNIUXJXfi9i8nmHj9eQY6 -otZaQ8H/7AQ77hPv01ha/5Lr7K7a8jcDR0G2l8ktCkEiu7vmpwIDAQABo4IBJTCCASEwEQYJYIZI -AYb4QgEBBAQDAgAHMGUGA1UdHwReMFwwWqBYoFakVDBSMQswCQYDVQQGEwJESzEVMBMGA1UEChMM -VERDIEludGVybmV0MR0wGwYDVQQLExRUREMgSW50ZXJuZXQgUm9vdCBDQTENMAsGA1UEAxMEQ1JM -MTArBgNVHRAEJDAigA8yMDAxMDQwNTE2MzMxN1qBDzIwMjEwNDA1MTcwMzE3WjALBgNVHQ8EBAMC -AQYwHwYDVR0jBBgwFoAUbGQBx/2FbazI2p5QCIUItTxWqFAwHQYDVR0OBBYEFGxkAcf9hW2syNqe -UAiFCLU8VqhQMAwGA1UdEwQFMAMBAf8wHQYJKoZIhvZ9B0EABBAwDhsIVjUuMDo0LjADAgSQMA0G -CSqGSIb3DQEBBQUAA4IBAQBOQ8zR3R0QGwZ/t6T609lN+yOfI1Rb5osvBCiLtSdtiaHsmGnc540m -gwV5dOy0uaOXwTUA/RXaOYE6lTGQ3pfphqiZdwzlWqCE/xIWrG64jcN7ksKsLtB9KOy282A4aW8+ -2ARVPp7MVdK6/rtHBNcK2RYKNCn1WBPVT8+PVkuzHu7TmHnaCB4Mb7j4Fifvwm899qNLPg7kbWzb -O0ESm70NRyN/PErQr8Cv9u8btRXE64PECV90i9kR+8JWsTz4cMo0jUNAE4z9mQNUecYu6oah9jrU -Cbz0vGbMPVjQV0kK7iXiQe4T+Zs4NNEA9X7nlB38aQNiuJkFBT1reBK9sG9l ------END CERTIFICATE----- - -UTN DATACorp SGC Root CA -======================== ------BEGIN CERTIFICATE----- -MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZ -BgNVBAMTElVUTiAtIERBVEFDb3JwIFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBa -MIGTMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4w -HAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRy -dXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ys -raP6LnD43m77VkIVni5c7yPeIbkFdicZD0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlo -wHDyUwDAXlCCpVZvNvlK4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA -9P4yPykqlXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulWbfXv -33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQABo4GrMIGoMAsGA1Ud -DwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRTMtGzz3/64PGgXYVOktKeRR20TzA9 -BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dD -LmNybDAqBgNVHSUEIzAhBggrBgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3 -DQEBBQUAA4IBAQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft -Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyjj98C5OBxOvG0 -I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVHKWss5nbZqSl9Mt3JNjy9rjXx -EZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwP -DPafepE39peC4N1xaf92P2BNPM/3mfnGV/TJVTl4uix5yaaIK/QI ------END CERTIFICATE----- - -UTN USERFirst Hardware Root CA -============================== ------BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE -BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl -IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd -BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx -OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0 -eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz -ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI -wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd -tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8 -i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf -Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw -gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF -lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF -UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF -BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW -XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2 -lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn -iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67 -nfhmqA== ------END CERTIFICATE----- - -Camerfirma Chambers of Commerce Root -==================================== ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx -NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp -cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn -MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC -AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU -xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH -NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW -DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV -d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud -EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v -cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P -AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh -bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD -VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi -fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD -L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN -UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n -ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1 -erfutGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- - -Camerfirma Global Chambersign Root -================================== ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe -QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i -ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx -NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt -YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg -MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw -ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J -1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O -by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl -6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c -8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/ -BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j -aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B -Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj -aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y -ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA -PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y -gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ -PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4 -IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes -t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- - -NetLock Notary (Class A) Root -============================= ------BEGIN CERTIFICATE----- -MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI -EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 -dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j -ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX -DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH -EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD -VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz -cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM -D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ -z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC -/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7 -tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6 -4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG -A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC -Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv -bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu -IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn -LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0 -ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz -IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh -IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu -b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh -bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg -Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp -bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5 -ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP -ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB -CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr -KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM -8CgHrTwXZoi1/baI ------END CERTIFICATE----- - -NetLock Business (Class B) Root -=============================== ------BEGIN CERTIFICATE----- -MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUxETAPBgNVBAcT -CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV -BAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQDEylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikg -VGFudXNpdHZhbnlraWFkbzAeFw05OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYD -VQQGEwJIVTERMA8GA1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRv -bnNhZ2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5ldExvY2sg -VXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB -iQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xKgZjupNTKihe5In+DCnVMm8Bp2GQ5o+2S -o/1bXHQawEfKOml2mrriRBf8TKPV/riXiK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr -1nGTLbO/CVRY7QbrqHvcQ7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNV -HQ8BAf8EBAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZ -RUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRh -dGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQuIEEgaGl0 -ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRv -c2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUg -YXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh -c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBz -Oi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6ZXNA -bmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhl -IHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2 -YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBj -cHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06sPgzTEdM -43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXan3BukxowOR0w2y7jfLKR -stE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKSNitjrFgBazMpUIaD8QFI ------END CERTIFICATE----- - -NetLock Express (Class C) Root -============================== ------BEGIN CERTIFICATE----- -MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUxETAPBgNVBAcT -CEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0b25zYWdpIEtmdC4xGjAYBgNV -BAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQDEytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBD -KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJ -BgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6 -dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMrTmV0TG9j -ayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzANBgkqhkiG9w0BAQEFAAOB -jQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNAOoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3Z -W3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63 -euyucYT2BDMIJTLrdKwWRMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQw -DgYDVR0PAQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEWggJN -RklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0YWxhbm9zIFN6b2xn -YWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBB -IGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBOZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1i -aXp0b3NpdGFzYSB2ZWRpLiBBIGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0 -ZWxlIGF6IGVsb2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs -ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25sYXBqYW4gYSBo -dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kga2VyaGV0byBheiBlbGxlbm9y -emVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4gSU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5k -IHRoZSB1c2Ugb2YgdGhpcyBjZXJ0aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQ -UyBhdmFpbGFibGUgYXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwg -YXQgY3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmYta3UzbM2 -xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2gpO0u9f38vf5NNwgMvOOW -gyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4Fp1hBWeAyNDYpQcCNJgEjTME1A== ------END CERTIFICATE----- - -XRamp Global CA Root -==================== ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCBgjELMAkGA1UE -BhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2Vj -dXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwHhcNMDQxMTAxMTcxNDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMx -HjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkg -U2VydmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS638eMpSe2OAtp87ZOqCwu -IR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCPKZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMx -foArtYzAQDsRhtDLooY2YKTVMIJt2W7QDxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FE -zG+gSqmUsE3a56k0enI4qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqs -AxcZZPRaJSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNViPvry -xS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASsjVy16bYbMDYGA1UdHwQvMC0wK6Ap -oCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMC -AQEwDQYJKoZIhvcNAQEFBQADggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc -/Kh4ZzXxHfARvbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt -qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLaIR9NmXmd4c8n -nxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSyi6mx5O+aGtA9aZnuqCij4Tyz -8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQO+7ETPTsJ3xCwnR8gooJybQDJbw= ------END CERTIFICATE----- - -Go Daddy Class 2 CA -=================== ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMY -VGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkG -A1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g -RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQAD -ggENADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv -2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+qN1j3hybX2C32 -qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiOr18SPaAIBQi2XKVlOARFmR6j -YGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmY -vLEHZ6IVDd2gWMZEewo+YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0O -BBYEFNLEsNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h/t2o -atTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMu -MTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwG -A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wim -PQoZ+YeAEW5p5JYXMP80kWNyOO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKt -I3lpjbi2Tc7PTMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ -HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mERdEr/VxqHD3VI -Ls9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5CufReYNnyicsbkqWletNw+vHX/b -vZ8= ------END CERTIFICATE----- - -Starfield Class 2 CA -==================== ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzElMCMGA1UEChMc -U3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZpZWxkIENsYXNzIDIg -Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBo -MQswCQYDVQQGEwJVUzElMCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAG -A1UECxMpU3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqG -SIb3DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf8MOh2tTY -bitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN+lq2cwQlZut3f+dZxkqZ -JRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVm -epsZGD3/cVE8MC5fvj13c7JdBmzDI1aaK4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSN -F4Azbl5KXZnJHoe0nRrA1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HF -MIHCMB0GA1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fRzt0f -hvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNo -bm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBDbGFzcyAyIENlcnRpZmljYXRpb24g -QXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGs -afPzWdqbAYcaT1epoXkJKtv3L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLM -PUxA2IGvd56Deruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl -xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynpVSJYACPq4xJD -KVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEYWQPJIrSPnNVeKtelttQKbfi3 -QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE----- - -StartCom Certification Authority -================================ ------BEGIN CERTIFICATE----- -MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN -U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu -ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 -NjM2WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk -LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg -U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y -o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ -Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d -eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt -2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z -6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ -osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ -untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc -UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT -37uMdBNSSwIDAQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE -FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9jZXJ0LnN0YXJ0 -Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3JsLnN0YXJ0Y29tLm9yZy9zZnNj -YS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFMBgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUH -AgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRw -Oi8vY2VydC5zdGFydGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYg -U3RhcnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlhYmlsaXR5 -LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2YgdGhlIFN0YXJ0Q29tIENl -cnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFpbGFibGUgYXQgaHR0cDovL2NlcnQuc3Rh -cnRjb20ub3JnL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilT -dGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOC -AgEAFmyZ9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8jhvh -3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUWFjgKXlf2Ysd6AgXm -vB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJzewT4F+irsfMuXGRuczE6Eri8sxHk -fY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3 -fsNrarnDy0RLrHiQi+fHLB5LEUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZ -EoalHmdkrQYuL6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq -yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuCO3NJo2pXh5Tl -1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6Vum0ABj6y6koQOdjQK/W/7HW/ -lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkyShNOsF/5oirpt9P/FlUQqmMGqz9IgcgA38coro -g14= ------END CERTIFICATE----- - -Taiwan GRCA -=========== ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQG -EwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4X -DTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1owPzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dv -dmVybm1lbnQgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qN -w8XRIePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1qgQdW8or5 -BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKyyhwOeYHWtXBiCAEuTk8O -1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAtsF/tnyMKtsc2AtJfcdgEWFelq16TheEfO -htX7MfP6Mb40qij7cEwdScevLJ1tZqa2jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wov -J5pGfaENda1UhhXcSTvxls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7 -Q3hub/FCVGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHKYS1t -B6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoHEgKXTiCQ8P8NHuJB -O9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThNXo+EHWbNxWCWtFJaBYmOlXqYwZE8 -lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1UdDgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNV -HRMEBTADAQH/MDkGBGcqBwAEMTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg2 -09yewDL7MTqKUWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ -TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyfqzvS/3WXy6Tj -Zwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaKZEk9GhiHkASfQlK3T8v+R0F2 -Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFEJPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlU -D7gsL0u8qV1bYH+Mh6XgUmMqvtg7hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6Qz -DxARvBMB1uUO07+1EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+Hbk -Z6MmnD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WXudpVBrkk -7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44VbnzssQwmSNOXfJIoRIM3BKQ -CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy -+fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS ------END CERTIFICATE----- - -Firmaprofesional Root CA -======================== ------BEGIN CERTIFICATE----- -MIIEVzCCAz+gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnTELMAkGA1UEBhMCRVMxIjAgBgNVBAcT -GUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMTOUF1dG9yaWRhZCBkZSBDZXJ0aWZp -Y2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODEmMCQGCSqGSIb3DQEJARYXY2FA -ZmlybWFwcm9mZXNpb25hbC5jb20wHhcNMDExMDI0MjIwMDAwWhcNMTMxMDI0MjIwMDAwWjCBnTEL -MAkGA1UEBhMCRVMxIjAgBgNVBAcTGUMvIE11bnRhbmVyIDI0NCBCYXJjZWxvbmExQjBABgNVBAMT -OUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2 -ODEmMCQGCSqGSIb3DQEJARYXY2FAZmlybWFwcm9mZXNpb25hbC5jb20wggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDnIwNvbyOlXnjOlSztlB5uCp4Bx+ow0Syd3Tfom5h5VtP8c9/Qit5V -j1H5WuretXDE7aTt/6MNbg9kUDGvASdYrv5sp0ovFy3Tc9UTHI9ZpTQsHVQERc1ouKDAA6XPhUJH -lShbz++AbOCQl4oBPB3zhxAwJkh91/zpnZFx/0GaqUC1N5wpIE8fUuOgfRNtVLcK3ulqTgesrBlf -3H5idPayBQC6haD9HThuy1q7hryUZzM1gywfI834yJFxzJeL764P3CkDG8A563DtwW4O2GcLiam8 -NeTvtjS0pbbELaW+0MOUJEjb35bTALVmGotmBQ/dPz/LP6pemkr4tErvlTcbAgMBAAGjgZ8wgZww -KgYDVR0RBCMwIYYfaHR0cDovL3d3dy5maXJtYXByb2Zlc2lvbmFsLmNvbTASBgNVHRMBAf8ECDAG -AQH/AgEBMCsGA1UdEAQkMCKADzIwMDExMDI0MjIwMDAwWoEPMjAxMzEwMjQyMjAwMDBaMA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUMwugZtHq2s7eYpMEKFK1FH84aLcwDQYJKoZIhvcNAQEFBQAD -ggEBAEdz/o0nVPD11HecJ3lXV7cVVuzH2Fi3AQL0M+2TUIiefEaxvT8Ub/GzR0iLjJcG1+p+o1wq -u00vR+L4OQbJnC4xGgN49Lw4xiKLMzHwFgQEffl25EvXwOaD7FnMP97/T2u3Z36mhoEyIwOdyPdf -wUpgpZKpsaSgYMN4h7Mi8yrrW6ntBas3D7Hi05V2Y1Z0jFhyGzflZKG+TQyTmAyX9odtsz/ny4Cm -7YjHX1BiAuiZdBbQ5rQ58SfLyEDW44YQqSMSkuBpQWOnryULwMWSyx6Yo1q6xTMPoJcB3X/ge9YG -VM+h4k0460tQtcsm9MracEpqoeJ5quGnM/b9Sh/22WA= ------END CERTIFICATE----- - -Swisscom Root CA 1 -================== ------BEGIN CERTIFICATE----- -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 -NY6E0F/6MBr1mmz0DlP5OlvRHA== ------END CERTIFICATE----- - -DigiCert Assured ID Root CA -=========================== ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw -IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx -MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL -ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO -9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy -UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW -/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy -oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf -GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF -66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq -hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc -EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn -SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i -8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- - -DigiCert Global Root CA -======================= ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw -HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw -MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 -dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn -TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 -BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H -4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y -7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB -o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm -8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF -BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr -EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt -tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 -UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- - -DigiCert High Assurance EV Root CA -================================== ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw -KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw -MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ -MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu -Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t -Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS -OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 -MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ -NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe -h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB -Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY -JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ -V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp -myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK -mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K ------END CERTIFICATE----- - -Certplus Class 2 Primary CA -=========================== ------BEGIN CERTIFICATE----- -MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAwPTELMAkGA1UE -BhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFzcyAyIFByaW1hcnkgQ0EwHhcN -OTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2Vy -dHBsdXMxGzAZBgNVBAMTEkNsYXNzIDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBANxQltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR -5aiRVhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyLkcAbmXuZ -Vg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCdEgETjdyAYveVqUSISnFO -YFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yasH7WLO7dDWWuwJKZtkIvEcupdM5i3y95e -e++U8Rs+yskhwcWYAqqi9lt3m/V+llU0HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRME -CDAGAQH/AgEKMAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJ -YIZIAYb4QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMuY29t -L0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/AN9WM2K191EBkOvD -P9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8yfFC82x/xXp8HVGIutIKPidd3i1R -TtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMRFcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+ -7UCmnYR0ObncHoUW2ikbhiMAybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW -//1IMwrh3KWBkJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 -l7+ijrRU ------END CERTIFICATE----- - -DST Root CA X3 -============== ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/MSQwIgYDVQQK -ExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMTDkRTVCBSb290IENBIFgzMB4X -DTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVowPzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1 -cmUgVHJ1c3QgQ28uMRcwFQYDVQQDEw5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmT -rE4Orz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEqOLl5CjH9 -UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9bxiqKqy69cK3FCxolkHRy -xXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40d -utolucbY38EVAjqr2m7xPi71XAicPNaDaeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQ -MA0GCSqGSIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69ikug -dB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXrAvHRAosZy5Q6XkjE -GB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZzR8srzJmwN0jP41ZL9c8PDHIyh8bw -RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS -fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- - -DST ACES CA X6 -============== ------BEGIN CERTIFICATE----- -MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT -MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha -MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE -CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI -DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa -pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow -GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy -MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu -Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy -dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU -CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2 -5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t -Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq -nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs -vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 -oKfN5XozNmr6mis= ------END CERTIFICATE----- - -TURKTRUST Certificate Services Provider Root 1 -============================================== ------BEGIN CERTIFICATE----- -MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP -MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 -acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx -MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg -U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB -TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC -aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX -yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i -Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ -8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 -W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME -BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 -sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE -q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy -B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY -nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H ------END CERTIFICATE----- - -TURKTRUST Certificate Services Provider Root 2 -============================================== ------BEGIN CERTIFICATE----- -MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP -MA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg -QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcN -MDUxMTA3MTAwNzU3WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVr -dHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEPMA0G -A1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls -acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqe -LCDe2JAOCtFp0if7qnefJ1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKI -x+XlZEdhR3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJQv2g -QrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGXJHpsmxcPbe9TmJEr -5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1pzpwACPI2/z7woQ8arBT9pmAPAgMB -AAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58SFq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/ntt -Rbj2hWyfIvwqECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4 -Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFzgw2lGh1uEpJ+ -hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotHuFEJjOp9zYhys2AzsfAKRO8P -9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LSy3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5 -UrbnBEI= ------END CERTIFICATE----- - -SwissSign Gold CA - G2 -====================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -SwissSign Silver CA - G2 -======================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority -======================================== ------BEGIN CERTIFICATE----- -MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMoR2VvVHJ1c3QgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgx -CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQ -cmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9AWbK7hWN -b6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjAZIVcFU2Ix7e64HXprQU9 -nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE07e9GceBrAqg1cmuXm2bgyxx5X9gaBGge -RwLmnWDiNpcB3841kt++Z8dtd1k7j53WkBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGt -tm/81w7a4DSwDRp35+MImO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJKoZI -hvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ16CePbJC/kRYkRj5K -Ts4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl4b7UVXGYNTq+k+qurUKykG/g/CFN -NWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6KoKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHa -Floxt/m0cYASSJlyc1pZU8FjUjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG -1riR/aYNKxoUAT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= ------END CERTIFICATE----- - -thawte Primary Root CA -====================== ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCBqTELMAkGA1UE -BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 -aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3 -MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwg -SW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMv -KGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAMT -FnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCs -oPD7gFnUnMekz52hWXMJEEUMDSxuaPFsW0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ -1CRfBsDMRJSUjQJib+ta3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGc -q/gcfomk6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6Sk/K -aAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94JNqR32HuHUETVPm4p -afs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XPr87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUF -AAOCAQEAeRHAS7ORtvzw6WfUDW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeE -uzLlQRHAd9mzYJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX -xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2/qxAeeWsEG89 -jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/LHbTY5xZ3Y+m4Q6gLkH3LpVH -z7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7jVaMaA== ------END CERTIFICATE----- - -VeriSign Class 3 Public Primary Certification Authority - G5 -============================================================ ------BEGIN CERTIFICATE----- -MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCByjELMAkGA1UE -BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO -ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk -IHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCB -yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2ln -biBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZvciBh -dXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmlt -YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKz -j/i5Vbext0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhD -Y2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/ -Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNHiDxpg8v+R70r -fk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/ -BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2Uv -Z2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy -aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKvMzEzMA0GCSqG -SIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzEp6B4Eq1iDkVwZMXnl2YtmAl+ -X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKE -KQsTb47bDN0lAtukixlE0kF6BWlKWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiC -Km0oHw0LxOXnGiYZ4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vE -ZV8NhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq ------END CERTIFICATE----- - -SecureTrust CA -============== ------BEGIN CERTIFICATE----- -MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy -dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe -BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX -OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t -DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH -GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b -01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH -ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj -aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ -KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu -SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf -mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ -nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR -3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE----- - -Secure Global CA -================ ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG -EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH -bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg -MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg -Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx -YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ -bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g -8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV -HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi -0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud -EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn -oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA -MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ -OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn -CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 -3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc -f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE----- - -COMODO Certification Authority -============================== ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE -BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG -A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 -dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb -MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD -T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH -+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww -xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV -4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA -1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI -rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k -b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC -AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP -OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc -IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN -+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== ------END CERTIFICATE----- - -Network Solutions Certificate Authority -======================================= ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQG -EwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydOZXR3b3Jr -IFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMx -MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwzc7MEL7xx -jOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPPOCwGJgl6cvf6UDL4wpPT -aaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rlmGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXT -crA/vGp97Eh/jcOrqnErU2lBUzS1sLnFBgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc -/Qzpf14Dl847ABSHJ3A4qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMB -AAGjgZcwgZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwubmV0c29sc3NsLmNv -bS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3JpdHkuY3JsMA0GCSqGSIb3DQEBBQUA -A4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc86fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q -4LqILPxFzBiwmZVRDuwduIj/h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/ -GGUsyfJj4akH/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD -ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------END CERTIFICATE----- - -WellsSecure Public Root Certificate Authority -============================================= ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM -F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw -NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN -MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl -bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD -VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1 -iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13 -i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8 -bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB -K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB -AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu -cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm -lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB -i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww -GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI -K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0 -bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj -qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es -E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ -tylv2G0xffX8oRAHh84vWdw+WNs= ------END CERTIFICATE----- - -COMODO ECC Certification Authority -================================== ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC -R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE -ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix -GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo -b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X -4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni -wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG -FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA -U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- - -IGC/A -===== ------BEGIN CERTIFICATE----- -MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD -VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE -Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy -MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI -EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT -STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2 -TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW -So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy -HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd -frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ -tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB -egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC -iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK -q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q -MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg -Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI -lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF -0mBWWg== ------END CERTIFICATE----- - -Security Communication EV RootCA1 -================================= ------BEGIN CERTIFICATE----- -MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE -BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl -Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO -/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX -WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z -ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4 -bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK -9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG -SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm -iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG -Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW -mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW -T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 ------END CERTIFICATE----- - -OISTE WISeKey Global Root GA CA -=============================== ------BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UE -BhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHlyaWdodCAoYykgMjAwNTEiMCAG -A1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBH -bG9iYWwgUm9vdCBHQSBDQTAeFw0wNTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYD -VQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIw -IAYDVQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5 -IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy0+zAJs9 -Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxRVVuuk+g3/ytr6dTqvirdqFEr12bDYVxg -Asj1znJ7O7jyTmUIms2kahnBAbtzptf2w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbD -d50kc3vkDIzh2TbhmYsFmQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ -/yxViJGg4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t94B3R -LoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ -KoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOxSPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vIm -MMkQyh2I+3QZH4VFvbBsUfk2ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4 -+vg1YFkCExh8vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa -hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY -okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0= ------END CERTIFICATE----- - -Microsec e-Szigno Root CA -========================= ------BEGIN CERTIFICATE----- -MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE -BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL -EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0 -MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz -dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT -GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG -d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N -oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc -QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ -PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb -MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG -IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD -VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3 -LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A -dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn -AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA -4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg -AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA -egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6 -Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO -PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv -c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h -cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw -IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT -WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV -MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER -MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp -Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal -HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT -nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE -aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a -86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK -yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB -S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU= ------END CERTIFICATE----- - -Certigna -======== ------BEGIN CERTIFICATE----- -MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw -EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 -MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI -Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q -XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH -GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p -ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg -DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf -Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ -tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ -BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J -SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA -hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ -ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu -PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY -1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw -WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE----- - -AC Ra\xC3\xADz Certic\xC3\xA1mara S.A. -====================================== ------BEGIN CERTIFICATE----- -MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNVBAYT -AkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRpZmljYWNpw7NuIERpZ2l0YWwg -LSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwaQUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4w -HhcNMDYxMTI3MjA0NjI5WhcNMzAwNDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+ -U29jaWVkYWQgQ2FtZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJh -IFMuQS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeGqentLhM0R7LQcNzJPNCN -yu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzLfDe3fezTf3MZsGqy2IiKLUV0qPezuMDU -2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQY5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU3 -4ojC2I+GdV75LaeHM/J4Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP -2yYe68yQ54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+bMMCm -8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48jilSH5L887uvDdUhf -HjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++EjYfDIJss2yKHzMI+ko6Kh3VOz3vCa -Mh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/ztA/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK -5lw1omdMEWux+IBkAC1vImHFrEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1b -czwmPS9KvqfJpxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE -AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCBlTCBkgYEVR0g -ADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFyYS5jb20vZHBjLzBaBggrBgEF -BQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW507WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2Ug -cHVlZGVuIGVuY29udHJhciBlbiBsYSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEf -AygPU3zmpFmps4p6xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuX -EpBcunvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/Jre7Ir5v -/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dpezy4ydV/NgIlqmjCMRW3 -MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42gzmRkBDI8ck1fj+404HGIGQatlDCIaR4 -3NAvO2STdPCWkPHv+wlaNECW8DYSwaN0jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wk -eZBWN7PGKX6jD/EpOe9+XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f -/RWmnkJDW2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/RL5h -RqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35rMDOhYil/SrnhLecU -Iw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxkBYn8eNZcLCZDqQ== ------END CERTIFICATE----- - -TC TrustCenter Class 2 CA II -============================ ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy -IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw -MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 -c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE -AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw -IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 -xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ -Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u -SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB -7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 -Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU -cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i -SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G -dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ -KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj -TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP -JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk -vQ== ------END CERTIFICATE----- - -TC TrustCenter Class 3 CA II -============================ ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy -IENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYw -MTEyMTQ0MTU3WhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 -c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UE -AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJWHt4bNwcwIi9v8Qbxq63W -yKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+QVl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo -6SI7dYnWRBpl8huXJh0obazovVkdKyT21oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZ -uV3bOx4a+9P/FRQI2AlqukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk -2ZyqBwi1Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NXXAek0CSnwPIA1DCB -7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 -Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU -cnVzdENlbnRlciUyMENsYXNzJTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i -SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlNirTzwppVMXzE -O2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8TtXqluJucsG7Kv5sbviRmEb8 -yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9 -IJqDnxrcOfHFcqMRA/07QlIp2+gB95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal -092Y+tTmBvTwtiBjS+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc -5A== ------END CERTIFICATE----- - -TC TrustCenter Universal CA I -============================= ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy -IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN -MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg -VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw -JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC -qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv -xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw -ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O -gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j -BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG -1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy -vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 -ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT -ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a -7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY ------END CERTIFICATE----- - -Deutsche Telekom Root CA 2 -========================== ------BEGIN CERTIFICATE----- -MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMT -RGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEG -A1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENBIDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5 -MjM1OTAwWjBxMQswCQYDVQQGEwJERTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0G -A1UECxMWVC1UZWxlU2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBS -b290IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEUha88EOQ5 -bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhCQN/Po7qCWWqSG6wcmtoI -KyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1MjwrrFDa1sPeg5TKqAyZMg4ISFZbavva4VhY -AUlfckE8FQYBjl2tqriTtM2e66foai1SNNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aK -Se5TBY8ZTNXeWHmb0mocQqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTV -jlsB9WoHtxa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAPBgNV -HRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAlGRZrTlk5ynr -E/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756AbrsptJh6sTtU6zkXR34ajgv8HzFZMQSy -zhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpaIzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8 -rZ7/gFnkm0W09juwzTkZmDLl6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4G -dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU -Cm26OWMohpLzGITY+9HPBVZkVw== ------END CERTIFICATE----- - -ComSign Secured CA -================== ------BEGIN CERTIFICATE----- -MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE -AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w -NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD -QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs -49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH -7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB -kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 -9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw -AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t -U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA -j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC -AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a -BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp -FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP -51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz -OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== ------END CERTIFICATE----- - -Cybertrust Global Root -====================== ------BEGIN CERTIFICATE----- -MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYGA1UEChMPQ3li -ZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBSb290MB4XDTA2MTIxNTA4 -MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQD -ExZDeWJlcnRydXN0IEdsb2JhbCBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -+Mi8vRRQZhP/8NN57CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW -0ozSJ8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2yHLtgwEZL -AfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iPt3sMpTjr3kfb1V05/Iin -89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNzFtApD0mpSPCzqrdsxacwOUBdrsTiXSZT -8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAYXSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2 -MDSgMqAwhi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3JsMB8G -A1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUAA4IBAQBW7wojoFRO -lZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMjWqd8BfP9IjsO0QbE2zZMcwSO5bAi -5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUxXOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2 -hO0j9n0Hq0V+09+zv+mKts2oomcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+T -X3EJIrduPuocA06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW -WL1WMRJOEcgh4LMRkWXbtKaIOM5V ------END CERTIFICATE----- - -ePKI Root Certification Authority -================================= ------BEGIN CERTIFICATE----- -MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG -EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg -Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx -MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq -MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs -IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi -lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv -qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX -12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O -WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ -ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao -lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ -vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi -Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi -MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH -ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 -1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq -KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV -xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP -NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r -GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE -xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx -gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy -sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD -BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE----- - -T\xc3\x9c\x42\xC4\xB0TAK UEKAE K\xC3\xB6k Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 - S\xC3\xBCr\xC3\xBCm 3 -============================================================================================================================= ------BEGIN CERTIFICATE----- -MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRSMRgwFgYDVQQH -DA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJpbGltc2VsIHZlIFRla25vbG9q -aWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSwVEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ry -b25payB2ZSBLcmlwdG9sb2ppIEFyYcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNV -BAsMGkthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUg -S8O2ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAeFw0wNzA4 -MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIxGDAWBgNVBAcMD0dlYnpl -IC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmlsaW1zZWwgdmUgVGVrbm9sb2ppayBBcmHF -n3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBUQUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZl -IEtyaXB0b2xvamkgQXJhxZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2Ft -dSBTZXJ0aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7ZrIFNl -cnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4hgb46ezzb8R1Sf1n68yJMlaCQvEhO -Eav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yKO7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1 -xnnRFDDtG1hba+818qEhTsXOfJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR -6Oqeyjh1jmKwlZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL -hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQIDAQABo0IwQDAd -BgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF -MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmPNOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4 -N5EY3ATIZJkrGG2AA1nJrvhY0D7twyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLT -y9LQQfMmNkqblWwM7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYh -LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M -dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI= ------END CERTIFICATE----- - -Buypass Class 2 CA 1 -==================== ------BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2 -MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh -c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M -cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83 -0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4 -0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R -uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P -AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV -1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt -7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2 -fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w -wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho ------END CERTIFICATE----- - -Buypass Class 3 CA 1 -==================== ------BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 -MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh -c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx -ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 -n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia -AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c -1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P -AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 -pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA -EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 -htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj -el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 ------END CERTIFICATE----- - -EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 -========================================================================== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -certSIGN ROOT CA -================ ------BEGIN CERTIFICATE----- -MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD -VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa -Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE -CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I -JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH -rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 -ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD -0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 -AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B -Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB -AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 -SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 -x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt -vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz -TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD ------END CERTIFICATE----- - -CNNIC ROOT -========== ------BEGIN CERTIFICATE----- -MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE -ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw -OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD -o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz -VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT -VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or -czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK -y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC -wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S -lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5 -Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM -O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8 -BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2 -G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m -mxE= ------END CERTIFICATE----- - -ApplicationCA - Japanese Government -=================================== ------BEGIN CERTIFICATE----- -MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT -SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw -MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl -cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4 -fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN -wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE -jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu -nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU -WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV -BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD -vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs -o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g -/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD -io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW -dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL -rosot4LKGAfmt1t06SAZf7IbiVQ= ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority - G3 -============================================= ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UE -BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA4IEdlb1RydXN0 -IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFy -eSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIz -NTk1OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAo -YykgMjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMT -LUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5j -K/BGvESyiaHAKAxJcCGVn2TAppMSAmUmhsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdE -c5IiaacDiGydY8hS2pgn5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3C -IShwiP/WJmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exALDmKu -dlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZChuOl1UcCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMR5yo6hTgMdHNxr -2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IBAQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9 -cr5HqQ6XErhK8WTTOd8lNNTBzU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbE -Ap7aDHdlDkQNkv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD -AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUHSJsMC8tJP33s -t/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2Gspki4cErx5z481+oghLrGREt ------END CERTIFICATE----- - -thawte Primary Root CA - G2 -=========================== ------BEGIN CERTIFICATE----- -MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDELMAkGA1UEBhMC -VVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMpIDIwMDcgdGhhd3RlLCBJbmMu -IC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3Qg -Q0EgLSBHMjAeFw0wNzExMDUwMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEV -MBMGA1UEChMMdGhhd3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBG -b3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAt -IEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/BebfowJPDQfGAFG6DAJS -LSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6papu+7qzcMBniKI11KOasf2twu8x+qi5 -8/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU -mtgAMADna3+FGO6Lts6KDPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUN -G4k8VIZ3KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41oxXZ3K -rr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== ------END CERTIFICATE----- - -thawte Primary Root CA - G3 -=========================== ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCBrjELMAkGA1UE -BhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2 -aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIwMDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxJDAiBgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0w -ODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh -d3RlLCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMTgwNgYD -VQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEkMCIG -A1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAsr8nLPvb2FvdeHsbnndmgcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2At -P0LMqmsywCPLLEHd5N/8YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC -+BsUa0Lfb1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS99irY -7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2SzhkGcuYMXDhpxwTW -vGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUkOQIDAQABo0IwQDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJ -KoZIhvcNAQELBQADggEBABpA2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweK -A3rD6z8KLFIWoCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu -t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7cKUGRIjxpp7sC -8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fMm7v/OeZWYdMKp8RcTGB7BXcm -er/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZuMdRAGmI0Nj81Aa6sY6A= ------END CERTIFICATE----- - -GeoTrust Primary Certification Authority - G2 -============================================= ------BEGIN CERTIFICATE----- -MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChjKSAyMDA3IEdlb1RydXN0IElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1 -OVowgZgxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg -MjAwNyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNVBAMTLUdl -b1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjB2MBAGByqGSM49AgEG -BSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcLSo17VDs6bl8VAsBQps8lL33KSLjHUGMc -KiEIfJo22Av+0SbFWDEwKCXzXV2juLaltJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+ -EVXVMAoGCCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGTqQ7m -ndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBuczrD6ogRLQy7rQkgu2 -npaqBA+K ------END CERTIFICATE----- - -VeriSign Universal Root Certification Authority -=============================================== ------BEGIN CERTIFICATE----- -MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCBvTELMAkGA1UE -BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO -ZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVk -IHVzZSBvbmx5MTgwNgYDVQQDEy9WZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9u -IEF1dGhvcml0eTAeFw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJV -UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdv -cmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNhbCBSb290IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj -1mCOkdeQmIN65lgZOIzF9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGP -MiJhgsWHH26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+HLL72 -9fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN/BMReYTtXlT2NJ8I -AfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPTrJ9VAMf2CGqUuV/c4DPxhGD5WycR -tPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0G -CCsGAQUFBwEMBGEwX6FdoFswWTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2O -a8PPgGrUSBgsexkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud -DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4sAPmLGd75JR3 -Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+seQxIcaBlVZaDrHC1LGmWazx -Y8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTx -P/jgdFcrGJ2BtMQo2pSXpXDrrB2+BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+P -wGZsY6rp2aQW9IHRlRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4 -mJO37M2CYfE45k+XmCpajQ== ------END CERTIFICATE----- - -VeriSign Class 3 Public Primary Certification Authority - G4 -============================================================ ------BEGIN CERTIFICATE----- -MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjELMAkGA1UEBhMC -VVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3 -b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVz -ZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU -cnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRo -b3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5 -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8 -Utpkmw4tXNherJI9/gHmGUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGz -rl0Bp3vefLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEw -HzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24u -Y29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMWkf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMD -A2gAMGUCMGYhDBgmYFo4e1ZC4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIx -AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== ------END CERTIFICATE----- - -NetLock Arany (Class Gold) Főtanúsítvány -============================================ ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G -A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 -dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB -cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx -MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO -ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv -biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 -c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu -0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw -/HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk -H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw -fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 -neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW -qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta -YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC -bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna -NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu -dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE----- - -Staat der Nederlanden Root CA - G2 -================================== ------BEGIN CERTIFICATE----- -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 -66+KAQ== ------END CERTIFICATE----- - -CA Disig -======== ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK -QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw -MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz -bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm -GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD -Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo -hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt -ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w -gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P -AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz -aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff -ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa -BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t -WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3 -mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/ -CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K -ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA -4Z7CRneC9VkGjCFMhwnN5ag= ------END CERTIFICATE----- - -Juur-SK -======= ------BEGIN CERTIFICATE----- -MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA -c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw -DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG -SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy -aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf -TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC -+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw -UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa -Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF -MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD -HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh -AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA -cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr -AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw -cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE -FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G -A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo -ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL -abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678 -IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh -Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2 -yyqcjg== ------END CERTIFICATE----- - -Hongkong Post Root CA 1 -======================= ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoT -DUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMB4XDTAzMDUx -NTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkGA1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25n -IFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1 -ApzQjVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEnPzlTCeqr -auh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjhZY4bXSNmO7ilMlHIhqqh -qZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9nnV0ttgCXjqQesBCNnLsak3c78QA3xMY -V18meMjWCnl3v/evt3a5pQuEF10Q6m/hq5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNV -HRMBAf8ECDAGAQH/AgEDMA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7i -h9legYsCmEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI37pio -l7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clBoiMBdDhViw+5Lmei -IAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJsEhTkYY2sEJCehFC78JZvRZ+K88ps -T/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpOfMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilT -c4afU9hDDl3WY4JxHYB0yvbiAmvZWg== ------END CERTIFICATE----- - -SecureSign RootCA11 -=================== ------BEGIN CERTIFICATE----- -MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi -SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS -b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw -KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 -cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL -TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO -wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq -g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP -O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA -bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX -t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh -OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r -bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ -Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 -y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 -lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= ------END CERTIFICATE----- - -ACEDICOM Root -============= ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -Verisign Class 3 Public Primary Certification Authority -======================================================= ------BEGIN CERTIFICATE----- -MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMx -FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5 -IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVow -XzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAz -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94 -f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Ol -hec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBABByUqkFFBky -CEHwxWsKzH4PIRnN5GfcX6kb5sroc50i2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWX -bj9T/UWZYB2oK0z5XqcJ2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/ -D/xwzoiQ ------END CERTIFICATE----- - -Microsec e-Szigno Root CA 2009 -============================== ------BEGIN CERTIFICATE----- -MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER -MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv -c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o -dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE -BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt -U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA -fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG -0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA -pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm -1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC -AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf -QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE -FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o -lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX -I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 -tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 -yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi -LXpUq3DDfSJlgnCW ------END CERTIFICATE----- - -E-Guven Kok Elektronik Sertifika Hizmet Saglayicisi -=================================================== ------BEGIN CERTIFICATE----- -MIIDtjCCAp6gAwIBAgIQRJmNPMADJ72cdpW56tustTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG -EwJUUjEoMCYGA1UEChMfRWxla3Ryb25payBCaWxnaSBHdXZlbmxpZ2kgQS5TLjE8MDoGA1UEAxMz -ZS1HdXZlbiBLb2sgRWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhZ2xheWljaXNpMB4XDTA3 -MDEwNDExMzI0OFoXDTE3MDEwNDExMzI0OFowdTELMAkGA1UEBhMCVFIxKDAmBgNVBAoTH0VsZWt0 -cm9uaWsgQmlsZ2kgR3V2ZW5saWdpIEEuUy4xPDA6BgNVBAMTM2UtR3V2ZW4gS29rIEVsZWt0cm9u -aWsgU2VydGlmaWthIEhpem1ldCBTYWdsYXlpY2lzaTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAMMSIJ6wXgBljU5Gu4Bc6SwGl9XzcslwuedLZYDBS75+PNdUMZTe1RK6UxYC6lhj71vY -8+0qGqpxSKPcEC1fX+tcS5yWCEIlKBHMilpiAVDV6wlTL/jDj/6z/P2douNffb7tC+Bg62nsM+3Y -jfsSSYMAyYuXjDtzKjKzEve5TfL0TW3H5tYmNwjy2f1rXKPlSFxYvEK+A1qBuhw1DADT9SN+cTAI -JjjcJRFHLfO6IxClv7wC90Nex/6wN1CZew+TzuZDLMN+DfIcQ2Zgy2ExR4ejT669VmxMvLz4Bcpk -9Ok0oSy1c+HCPujIyTQlCFzz7abHlJ+tiEMl1+E5YP6sOVkCAwEAAaNCMEAwDgYDVR0PAQH/BAQD -AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJ/uRLOU1fqRTy7ZVZoEVtstxNulMA0GCSqG -SIb3DQEBBQUAA4IBAQB/X7lTW2M9dTLn+sR0GstG30ZpHFLPqk/CaOv/gKlR6D1id4k9CnU58W5d -F4dvaAXBlGzZXd/aslnLpRCKysw5zZ/rTt5S/wzw9JKp8mxTq5vSR6AfdPebmvEvFZ96ZDAYBzwq -D2fK/A+JYZ1lpTzlvBNbCNvj/+27BrtqBrF6T2XGgv0enIu1De5Iu7i9qgi0+6N8y5/NkHZchpZ4 -Vwpm+Vganf2XKWDeEaaQHBkc7gGWIjQ0LpH5t8Qn0Xvmv/uARFoW5evg1Ao4vOSR49XrXMGs3xtq -fJ7lddK2l4fbzIcrQzqECK+rPNv3PGYxhrCdU3nt+CPeQuMtgvEP5fqX ------END CERTIFICATE----- - -GlobalSign Root CA - R3 -======================= ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv -YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh -bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT -aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln -bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt -iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ -0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 -rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl -OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 -xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE -FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 -lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 -EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E -bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 -YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r -kpeDMdmztcpHWD9f ------END CERTIFICATE----- - -Autoridad de Certificacion Firmaprofesional CIF A62634068 -========================================================= ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA -BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 -MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw -QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB -NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD -Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P -B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY -7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH -ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI -plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX -MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX -LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK -bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU -vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud -EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH -DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp -cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA -bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx -ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx -51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk -R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP -T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f -Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl -osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR -crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR -saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD -KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi -6Et8Vcad+qMUu2WFbm5PEn4KPJ2V ------END CERTIFICATE----- - -Izenpe.com -========== ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -Chambers of Commerce Root - 2008 -================================ ------BEGIN CERTIFICATE----- -MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xKTAnBgNVBAMTIENoYW1iZXJzIG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEy -Mjk1MFoXDTM4MDczMTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNl -ZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQF -EwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJl -cnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW928sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKA -XuFixrYp4YFs8r/lfTJqVKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorj -h40G072QDuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR5gN/ -ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfLZEFHcpOrUMPrCXZk -NNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05aSd+pZgvMPMZ4fKecHePOjlO+Bd5g -D2vlGts/4+EhySnB8esHnFIbAURRPHsl18TlUlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331 -lubKgdaX8ZSD6e2wsWsSaR6s+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ -0wlf2eOKNcx5Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj -ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAxhduub+84Mxh2 -EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNVHQ4EFgQU+SSsD7K1+HnA+mCI -G8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJ -BgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNh -bWVyZmlybWEuY29tL2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENh -bWVyZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDiC -CQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUH -AgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAJASryI1 -wqM58C7e6bXpeHxIvj99RZJe6dqxGfwWPJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH -3qLPaYRgM+gQDROpI9CF5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbU -RWpGqOt1glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaHFoI6 -M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2pSB7+R5KBWIBpih1 -YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MDxvbxrN8y8NmBGuScvfaAFPDRLLmF -9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QGtjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcK -zBIKinmwPQN/aUv0NCB9szTqjktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvG -nrDQWzilm1DefhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg -OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZd0jQ ------END CERTIFICATE----- - -Global Chambersign Root - 2008 -============================== ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYDVQQGEwJFVTFD -MEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNv -bS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMu -QS4xJzAlBgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMx -NDBaFw0zODA3MzExMjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUg -Y3VycmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJ -QTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMDf -VtPkOpt2RbQT2//BthmLN0EYlVJH6xedKYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXf -XjaOcNFccUMd2drvXNL7G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0 -ZJJ0YPP2zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4ddPB -/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyGHoiMvvKRhI9lNNgA -TH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2Id3UwD2ln58fQ1DJu7xsepeY7s2M -H/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3VyJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfe -Ox2YItaswTXbo6Al/3K1dh3ebeksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSF -HTynyQbehP9r6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsogzCtLkykPAgMB -AAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQWBBS5CcqcHtvTbDprru1U8VuT -BjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDprru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UE -BhMCRVUxQzBBBgNVBAcTOk1hZHJpZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJm -aXJtYS5jb20vYWRkcmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJm -aXJtYSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiCCQDJzdPp -1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0 -dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZIhvcNAQEFBQADggIBAICIf3DekijZBZRG -/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZUohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6 -ReAJ3spED8IXDneRRXozX1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/s -dZ7LoR/xfxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVza2Mg -9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yydYhz2rXzdpjEetrHH -foUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMdSqlapskD7+3056huirRXhOukP9Du -qqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9OAP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETr -P3iZ8ntxPjzxmKfFGBI/5rsoM0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVq -c5iJWzouE4gev8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- - -Go Daddy Root Certificate Authority - G2 -======================================== ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu -MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 -MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G -A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq -9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD -+qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd -fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl -NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 -BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac -vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r -5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV -N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 ------END CERTIFICATE----- - -Starfield Root Certificate Authority - G2 -========================================= ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s -b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 -eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw -DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg -VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB -dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv -W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs -bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk -N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf -ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU -JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol -TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx -4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw -F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ -c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- - -Starfield Services Root Certificate Authority - G2 -================================================== ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT -B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s -b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl -IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV -BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT -dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg -Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 -h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa -hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP -LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB -rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG -SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP -E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy -xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd -iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza -YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 ------END CERTIFICATE----- - -AffirmTrust Commercial -====================== ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw -MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly -bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb -DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV -C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 -BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww -MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV -HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG -hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi -qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv -0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh -sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- - -AffirmTrust Networking -====================== ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw -MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly -bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE -Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI -dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 -/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb -h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV -HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu -UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 -12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 -WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 -/ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- - -AffirmTrust Premium -=================== ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS -BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy -OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy -dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn -BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV -5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs -+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd -GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R -p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI -S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 -6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 -/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo -+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv -MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC -6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S -L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK -+4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV -BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg -IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 -g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb -zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== ------END CERTIFICATE----- - -AffirmTrust Premium ECC -======================= ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV -BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx -MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U -cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ -N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW -BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK -BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X -57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM -eQ== ------END CERTIFICATE----- - -Certum Trusted Network CA -========================= ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK -ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy -MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU -ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC -l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J -J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 -fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 -cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB -Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw -DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj -jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 -mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj -Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- - -Certinomis - Autorité Racine -============================= ------BEGIN CERTIFICATE----- -MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK -Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg -LSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkG -A1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYw -JAYDVQQDDB1DZXJ0aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jYF1AMnmHa -wE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N8y4oH3DfVS9O7cdxbwly -Lu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWerP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw -2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92N -jMD2AR5vpTESOH2VwnHu7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9q -c1pkIuVC28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6lSTC -lrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1Enn1So2+WLhl+HPNb -xxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB0iSVL1N6aaLwD4ZFjliCK0wi1F6g -530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql095gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna -4NH4+ej9Uji29YnfAgMBAAGjWzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBQNjLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ -KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9sov3/4gbIOZ/x -WqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZMOH8oMDX/nyNTt7buFHAAQCva -R6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40 -nJ+U8/aGH88bc62UeYdocMMzpXDn2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1B -CxMjidPJC+iKunqjo3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjv -JL1vnxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG5ERQL1TE -qkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWqpdEdnV1j6CTmNhTih60b -WfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZbdsLLO7XSAPCjDuGtbkD326C00EauFddE -wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/ -vgt2Fl43N+bYdJeimUV5 ------END CERTIFICATE----- - -Root CA Generalitat Valenciana -============================== ------BEGIN CERTIFICATE----- -MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE -ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290 -IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3 -WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE -CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2 -F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B -ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ -D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte -JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB -AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n -dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB -ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl -AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA -YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy -AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA -aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt -AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA -YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu -AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA -OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0 -dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV -BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G -A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S -b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh -TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz -Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63 -NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH -iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt -+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM= ------END CERTIFICATE----- - -A-Trust-nQual-03 -================ ------BEGIN CERTIFICATE----- -MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJBVDFIMEYGA1UE -Cgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBpbSBlbGVrdHIuIERhdGVudmVy -a2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5RdWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5R -dWFsLTAzMB4XDTA1MDgxNzIyMDAwMFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgw -RgYDVQQKDD9BLVRydXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0 -ZW52ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMMEEEtVHJ1 -c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtPWFuA/OQO8BBC4SA -zewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUjlUC5B3ilJfYKvUWG6Nm9wASOhURh73+n -yfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZznF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPE -SU7l0+m0iKsMrmKS1GWH2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4 -iHQF63n1k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs2e3V -cuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0OBAoECERqlWdV -eRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAVdRU0VlIXLOThaq/Yy/kgM40 -ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fGKOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmr -sQd7TZjTXLDR8KdCoLXEjq/+8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZd -JXDRZslo+S4RFGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS -mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmEDNuxUCAKGkq6 -ahq97BvIxYSazQ== ------END CERTIFICATE----- - -TWCA Root Certification Authority -================================= ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ -VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG -EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB -IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx -QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC -oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP -4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r -y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG -9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC -mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW -QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY -T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny -Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE----- - -Security Communication RootCA2 -============================== ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc -U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh -dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC -SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy -aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ -+T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R -3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV -spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K -EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 -QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB -CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj -u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk -3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q -tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 -mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE----- - -EC-ACC -====== ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE -BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w -ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD -VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE -CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT -BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 -MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt -SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl -Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh -cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK -w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT -ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 -HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a -E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw -0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD -VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 -Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l -dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ -lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa -Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe -l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 -E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D -5EI= ------END CERTIFICATE----- - -Hellenic Academic and Research Institutions RootCA 2011 -======================================================= ------BEGIN CERTIFICATE----- -MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT -O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y -aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT -AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z -IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo -IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI -1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa -71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u -8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH -3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/ -MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8 -MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu -b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt -XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 -TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD -/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N -7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4 ------END CERTIFICATE----- - -Actalis Authentication Root CA -============================== ------BEGIN CERTIFICATE----- -MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM -BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE -AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky -MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz -IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 -IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ -wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa -by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 -zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f -YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 -oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l -EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 -hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 -EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 -jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY -iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt -ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI -WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 -JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx -K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ -Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC -4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo -2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz -lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem -OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 -vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE----- - -Trustis FPS Root CA -=================== ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQG -EwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQLExNUcnVzdGlzIEZQUyBSb290 -IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTExMzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNV -BAoTD1RydXN0aXMgTGltaXRlZDEcMBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQ -RUN+AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihHiTHcDnlk -H5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjjvSkCqPoc4Vu5g6hBSLwa -cY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zt -o3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlBOrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEA -AaNTMFEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAd -BgNVHQ4EFgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01GX2c -GE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmWzaD+vkAMXBJV+JOC -yinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP41BIy+Q7DsdwyhEQsb8tGD+pmQQ9P -8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZEf1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHV -l/9D7S3B2l0pKoU/rGXuhg8FjZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYl -iB6XzCGcKQENZetX2fNXlrtIzYE= ------END CERTIFICATE----- - -StartCom Certification Authority -================================ ------BEGIN CERTIFICATE----- -MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMN -U3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmlu -ZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0 -NjM3WhcNMzYwOTE3MTk0NjM2WjB9MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRk -LjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMg -U3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZkpMyONvg45iPwbm2xPN1y -o4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rfOQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/ -Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/CJi/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/d -eMotHweXMAEtcnn6RtYTKqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt -2PZE4XNiHzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMMAv+Z -6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w+2OqqGwaVLRcJXrJ -osmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/ -untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVc -UjyJthkqcwEKDwOzEmDyei+B26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT -37uMdBNSSwIDAQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD -VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFulF2mHMMo0aEPQ -Qa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCCATgwLgYIKwYBBQUHAgEWImh0 -dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cu -c3RhcnRzc2wuY29tL2ludGVybWVkaWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENv -bW1lcmNpYWwgKFN0YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0 -aGUgc2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93d3cuc3RhcnRzc2wuY29t -L3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBG -cmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5 -fPGFf59Jb2vKXfuM/gTFwWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWm -N3PH/UvSTa0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst0OcN -Org+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNcpRJvkrKTlMeIFw6T -tn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKlCcWw0bdT82AUuoVpaiF8H3VhFyAX -e2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVFP0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA -2MFrLH9ZXF2RsXAiV+uKa0hK1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBs -HvUwyKMQ5bLmKhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE -JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ8dCAWZvLMdib -D4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnmfyWl8kgAwKQB2j8= ------END CERTIFICATE----- - -StartCom Certification Authority G2 -=================================== ------BEGIN CERTIFICATE----- -MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMN -U3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -RzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UE -ChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgRzIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8O -o1XJJZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsDvfOpL9HG -4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnooD/Uefyf3lLE3PbfHkffi -Aez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/Q0kGi4xDuFby2X8hQxfqp0iVAXV16iul -Q5XqFYSdCI0mblWbq9zSOdIxHWDirMxWRST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbs -O+wmETRIjfaAKxojAuuKHDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8H -vKTlXcxNnw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM0D4L -nMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/iUUjXuG+v+E5+M5iS -FGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9Ha90OrInwMEePnWjFqmveiJdnxMa -z6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHgTuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJ -KoZIhvcNAQELBQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K -2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfXUfEpY9Z1zRbk -J4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl6/2o1PXWT6RbdejF0mCy2wl+ -JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG -/+gyRr61M3Z3qAFdlsHB1b6uJcDJHgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTc -nIhT76IxW1hPkWLIwpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/Xld -blhYXzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5lIxKVCCIc -l85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoohdVddLHRDiBYmxOlsGOm -7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulrso8uBtjRkcfGEvRM/TAXw8HaOFvjqerm -obp573PYtlNXLfbQ4ddI ------END CERTIFICATE----- - -Buypass Class 2 Root CA -======================= ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X -DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 -eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 -g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn -9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b -/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU -CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff -awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI -zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn -Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX -Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs -M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF -AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s -A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI -osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S -aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd -DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD -LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 -oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC -wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS -CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN -rJgWVqA= ------END CERTIFICATE----- - -Buypass Class 3 Root CA -======================= ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X -DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 -eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH -sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR -5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh -7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ -ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH -2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV -/afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ -RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA -Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq -j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF -AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV -cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G -uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG -Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 -ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 -KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz -6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug -UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe -eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi -Cp/HuZc= ------END CERTIFICATE----- - -T-TeleSec GlobalRoot Class 3 -============================ ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM -IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU -cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx -MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz -dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD -ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK -9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU -NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF -iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W -0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr -AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb -fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT -ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h -P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml -e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== ------END CERTIFICATE----- - -EE Certification Centre Root CA -=============================== ------BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQG -EwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEoMCYGA1UEAwwfRUUgQ2Vy -dGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIw -MTAxMDMwMTAxMDMwWhgPMjAzMDEyMTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlB -UyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRy -ZSBSb290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUyeuuOF0+W2Ap7kaJjbMeM -TC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvObntl8jixwKIy72KyaOBhU8E2lf/slLo2 -rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIwWFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw -93X2PaRka9ZP585ArQ/dMtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtN -P2MbRMNE1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/zQas8fElyalL1BSZ -MEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYBBQUHAwMGCCsGAQUFBwMEBggrBgEF -BQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEFBQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+Rj -xY6hUFaTlrg4wCQiZrxTFGGVv9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqM -lIpPnTX/dqQGE5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u -uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIWiAYLtqZLICjU -3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/vGVCJYMzpJJUPwssd8m92kMfM -dcGWxZ0= ------END CERTIFICATE----- - -TURKTRUST Certificate Services Provider Root 2007 -================================================= ------BEGIN CERTIFICATE----- -MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOcUktUUlVTVCBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJUUjEP -MA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUg -QmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4X -DTA3MTIyNTE4MzcxOVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxl -a3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMCVFIxDzAN -BgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp -bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4gKGMpIEFyYWzEsWsgMjAwNzCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9N -YvDdE3ePYakqtdTyuTFYKTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQv -KUmi8wUG+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveGHtya -KhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6PIzdezKKqdfcYbwnT -rqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M733WB2+Y8a+xwXrXgTW4qhe04MsC -AwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHkYb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/s -Px+EnWVUXKgWAkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I -aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5mxRZNTZPz/OO -Xl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsaXRik7r4EW5nVcV9VZWRi1aKb -BFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZqxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAK -poRq0Tl9 ------END CERTIFICATE----- - -D-TRUST Root Class 3 CA 2 2009 -============================== ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe -Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE -LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD -ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA -BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv -KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z -p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC -AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ -4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y -eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw -MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G -PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw -OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm -2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 -o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV -dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph -X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE----- - -D-TRUST Root Class 3 CA 2 EV 2009 -================================= ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw -OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK -DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw -OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS -egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh -zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T -7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 -sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 -11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv -cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v -ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El -MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp -b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh -c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ -PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 -nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX -ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA -NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv -w9y4AyHqnxbxLFS1 ------END CERTIFICATE----- - -PSCProcert -========== ------BEGIN CERTIFICATE----- -MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1dG9yaWRhZCBk -ZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9sYW5vMQswCQYDVQQGEwJWRTEQ -MA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlzdHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lz -dGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBl -cmludGVuZGVuY2lhIGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUw -IwYJKoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEwMFoXDTIw -MTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHByb2NlcnQubmV0LnZlMQ8w -DQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGExKjAoBgNVBAsTIVByb3ZlZWRvciBkZSBD -ZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZp -Y2FjaW9uIEVsZWN0cm9uaWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIw -DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo97BVC -wfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74BCXfgI8Qhd19L3uA -3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38GieU89RLAu9MLmV+QfI4tL3czkkoh -RqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmO -EO8GqQKJ/+MMbpfg353bIdD0PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG2 -0qCZyFSTXai20b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH -0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/6mnbVSKVUyqU -td+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1mv6JpIzi4mWCZDlZTOpx+FIyw -Bm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvp -r2uKGcfLFFb14dq12fy/czja+eevbqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/ -AgEBMDcGA1UdEgQwMC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAz -Ni0wMB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFDgBStuyId -xuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0b3JpZGFkIGRlIENlcnRp -ZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xhbm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQH -EwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5h -Y2lvbmFsIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5k -ZW5jaWEgZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkqhkiG -9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQDAgEGME0GA1UdEQRG -MESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0wMDAwMDKgGwYFYIZeAgKgEgwQUklG -LUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEagRKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52 -ZS9sY3IvQ0VSVElGSUNBRE8tUkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNy -YWl6LnN1c2NlcnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v -Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsGAQUFBwIBFh5o -dHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcNAQELBQADggIBACtZ6yKZu4Sq -T96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmN -g7+mvTV+LFwxNG9s2/NkAZiqlCxB3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4q -uxtxj7mkoP3YldmvWb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1 -n8GhHVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHmpHmJWhSn -FFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXzsOfIt+FTvZLm8wyWuevo -5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bEqCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq -3TNWOByyrYDT13K9mmyZY+gAu0F2BbdbmRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5 -poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y -eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km ------END CERTIFICATE----- - -China Internet Network Information Center EV Certificates Root -============================================================== ------BEGIN CERTIFICATE----- -MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV -BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D -aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg -Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG -A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM -PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl -cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y -jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV -98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H -klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23 -KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC -7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD -glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5 -0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM -7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws -ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0 -5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8= ------END CERTIFICATE----- - -Swisscom Root CA 2 -================== ------BEGIN CERTIFICATE----- -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 -5OfNeOI5wSsSnqaeG8XmDtkx2Q== ------END CERTIFICATE----- - -Swisscom Root EV CA 2 -===================== ------BEGIN CERTIFICATE----- -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 -l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc= ------END CERTIFICATE----- - -CA Disig Root R1 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy -3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8 -u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2 -m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk -CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa -YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6 -vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL -LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX -ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is -XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ -04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR -xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B -LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM -CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb -VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85 -YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS -ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix -lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N -UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ -a7+h89n07eLw4+1knj0vllJPgFOL ------END CERTIFICATE----- - -CA Disig Root R2 -================ ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw -EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp -ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx -EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp -c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC -w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia -xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 -A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S -GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV -g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa -5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE -koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A -Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i -Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u -Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM -tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV -sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je -dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 -1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx -mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 -utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 -sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg -UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV -7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE----- - -ACCVRAIZ1 -========= ------BEGIN CERTIFICATE----- -MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB -SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 -MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH -UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM -jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 -RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD -aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ -0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG -WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 -8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR -5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J -9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK -Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw -Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu -Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 -VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM -Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA -QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh -AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA -YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj -AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA -IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk -aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 -dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 -MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI -hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E -R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN -YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 -nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ -TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 -sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h -I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg -Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd -3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p -EfbRD0tVNEYqi4Y7 ------END CERTIFICATE----- - -TWCA Global Root CA -=================== ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT -CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD -QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK -EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg -Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C -nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV -r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR -Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV -tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W -KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 -sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p -yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn -kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI -zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC -AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g -cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn -LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M -8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg -/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg -lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP -A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m -i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 -EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 -zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= ------END CERTIFICATE----- diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php deleted file mode 100644 index dbd4c1841..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/StaticClient.php +++ /dev/null @@ -1,157 +0,0 @@ -createRequest($method, $url, null, null, $options); - - if (isset($options['stream'])) { - if ($options['stream'] instanceof StreamRequestFactoryInterface) { - return $options['stream']->fromRequest($request); - } elseif ($options['stream'] == true) { - $streamFactory = new PhpStreamRequestFactory(); - return $streamFactory->fromRequest($request); - } - } - - return $request->send(); - } - - /** - * Send a GET request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function get($url, $options = array()) - { - return self::request('GET', $url, $options); - } - - /** - * Send a HEAD request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function head($url, $options = array()) - { - return self::request('HEAD', $url, $options); - } - - /** - * Send a DELETE request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function delete($url, $options = array()) - { - return self::request('DELETE', $url, $options); - } - - /** - * Send a POST request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function post($url, $options = array()) - { - return self::request('POST', $url, $options); - } - - /** - * Send a PUT request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function put($url, $options = array()) - { - return self::request('PUT', $url, $options); - } - - /** - * Send a PATCH request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function patch($url, $options = array()) - { - return self::request('PATCH', $url, $options); - } - - /** - * Send an OPTIONS request - * - * @param string $url URL of the request - * @param array $options Array of request options - * - * @return \Guzzle\Http\Message\Response - * @see Guzzle::request for a list of available options - */ - public static function options($url, $options = array()) - { - return self::request('OPTIONS', $url, $options); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php b/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php deleted file mode 100644 index 6a4e77245..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/Url.php +++ /dev/null @@ -1,554 +0,0 @@ - null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, - 'user' => null, 'pass' => null, 'fragment' => null); - - if (false === ($parts = parse_url($url))) { - throw new InvalidArgumentException('Was unable to parse malformed url: ' . $url); - } - - $parts += $defaults; - - // Convert the query string into a QueryString object - if ($parts['query'] || 0 !== strlen($parts['query'])) { - $parts['query'] = QueryString::fromString($parts['query']); - } - - return new static($parts['scheme'], $parts['host'], $parts['user'], - $parts['pass'], $parts['port'], $parts['path'], $parts['query'], - $parts['fragment']); - } - - /** - * Build a URL from parse_url parts. The generated URL will be a relative URL if a scheme or host are not provided. - * - * @param array $parts Array of parse_url parts - * - * @return string - */ - public static function buildUrl(array $parts) - { - $url = $scheme = ''; - - if (isset($parts['scheme'])) { - $scheme = $parts['scheme']; - $url .= $scheme . ':'; - } - - if (isset($parts['host'])) { - $url .= '//'; - if (isset($parts['user'])) { - $url .= $parts['user']; - if (isset($parts['pass'])) { - $url .= ':' . $parts['pass']; - } - $url .= '@'; - } - - $url .= $parts['host']; - - // Only include the port if it is not the default port of the scheme - if (isset($parts['port']) - && !(($scheme == 'http' && $parts['port'] == 80) || ($scheme == 'https' && $parts['port'] == 443)) - ) { - $url .= ':' . $parts['port']; - } - } - - // Add the path component if present - if (isset($parts['path']) && 0 !== strlen($parts['path'])) { - // Always ensure that the path begins with '/' if set and something is before the path - if ($url && $parts['path'][0] != '/' && substr($url, -1) != '/') { - $url .= '/'; - } - $url .= $parts['path']; - } - - // Add the query string if present - if (isset($parts['query'])) { - $url .= '?' . $parts['query']; - } - - // Ensure that # is only added to the url if fragment contains anything. - if (isset($parts['fragment'])) { - $url .= '#' . $parts['fragment']; - } - - return $url; - } - - /** - * Create a new URL from URL parts - * - * @param string $scheme Scheme of the URL - * @param string $host Host of the URL - * @param string $username Username of the URL - * @param string $password Password of the URL - * @param int $port Port of the URL - * @param string $path Path of the URL - * @param QueryString|array|string $query Query string of the URL - * @param string $fragment Fragment of the URL - */ - public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) - { - $this->scheme = $scheme; - $this->host = $host; - $this->port = $port; - $this->username = $username; - $this->password = $password; - $this->fragment = $fragment; - if (!$query) { - $this->query = new QueryString(); - } else { - $this->setQuery($query); - } - $this->setPath($path); - } - - /** - * Clone the URL - */ - public function __clone() - { - $this->query = clone $this->query; - } - - /** - * Returns the URL as a URL string - * - * @return string - */ - public function __toString() - { - return self::buildUrl($this->getParts()); - } - - /** - * Get the parts of the URL as an array - * - * @return array - */ - public function getParts() - { - $query = (string) $this->query; - - return array( - 'scheme' => $this->scheme, - 'user' => $this->username, - 'pass' => $this->password, - 'host' => $this->host, - 'port' => $this->port, - 'path' => $this->getPath(), - 'query' => $query !== '' ? $query : null, - 'fragment' => $this->fragment, - ); - } - - /** - * Set the host of the request. - * - * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) - * - * @return Url - */ - public function setHost($host) - { - if (strpos($host, ':') === false) { - $this->host = $host; - } else { - list($host, $port) = explode(':', $host); - $this->host = $host; - $this->setPort($port); - } - - return $this; - } - - /** - * Get the host part of the URL - * - * @return string - */ - public function getHost() - { - return $this->host; - } - - /** - * Set the scheme part of the URL (http, https, ftp, etc) - * - * @param string $scheme Scheme to set - * - * @return Url - */ - public function setScheme($scheme) - { - if ($this->scheme == 'http' && $this->port == 80) { - $this->port = null; - } elseif ($this->scheme == 'https' && $this->port == 443) { - $this->port = null; - } - - $this->scheme = $scheme; - - return $this; - } - - /** - * Get the scheme part of the URL - * - * @return string - */ - public function getScheme() - { - return $this->scheme; - } - - /** - * Set the port part of the URL - * - * @param int $port Port to set - * - * @return Url - */ - public function setPort($port) - { - $this->port = $port; - - return $this; - } - - /** - * Get the port part of the URl. Will return the default port for a given scheme if no port has been set. - * - * @return int|null - */ - public function getPort() - { - if ($this->port) { - return $this->port; - } elseif ($this->scheme == 'http') { - return 80; - } elseif ($this->scheme == 'https') { - return 443; - } - - return null; - } - - /** - * Set the path part of the URL - * - * @param array|string $path Path string or array of path segments - * - * @return Url - */ - public function setPath($path) - { - static $pathReplace = array(' ' => '%20', '?' => '%3F'); - if (is_array($path)) { - $path = '/' . implode('/', $path); - } - - $this->path = strtr($path, $pathReplace); - - return $this; - } - - /** - * Normalize the URL so that double slashes and relative paths are removed - * - * @return Url - */ - public function normalizePath() - { - if (!$this->path || $this->path == '/' || $this->path == '*') { - return $this; - } - - $results = array(); - $segments = $this->getPathSegments(); - foreach ($segments as $segment) { - if ($segment == '..') { - array_pop($results); - } elseif ($segment != '.' && $segment != '') { - $results[] = $segment; - } - } - - // Combine the normalized parts and add the leading slash if needed - $this->path = ($this->path[0] == '/' ? '/' : '') . implode('/', $results); - - // Add the trailing slash if necessary - if ($this->path != '/' && end($segments) == '') { - $this->path .= '/'; - } - - return $this; - } - - /** - * Add a relative path to the currently set path. - * - * @param string $relativePath Relative path to add - * - * @return Url - */ - public function addPath($relativePath) - { - if ($relativePath != '/' && is_string($relativePath) && strlen($relativePath) > 0) { - // Add a leading slash if needed - if ($relativePath[0] != '/') { - $relativePath = '/' . $relativePath; - } - $this->setPath(str_replace('//', '/', $this->path . $relativePath)); - } - - return $this; - } - - /** - * Get the path part of the URL - * - * @return string - */ - public function getPath() - { - return $this->path; - } - - /** - * Get the path segments of the URL as an array - * - * @return array - */ - public function getPathSegments() - { - return array_slice(explode('/', $this->getPath()), 1); - } - - /** - * Set the password part of the URL - * - * @param string $password Password to set - * - * @return Url - */ - public function setPassword($password) - { - $this->password = $password; - - return $this; - } - - /** - * Get the password part of the URL - * - * @return null|string - */ - public function getPassword() - { - return $this->password; - } - - /** - * Set the username part of the URL - * - * @param string $username Username to set - * - * @return Url - */ - public function setUsername($username) - { - $this->username = $username; - - return $this; - } - - /** - * Get the username part of the URl - * - * @return null|string - */ - public function getUsername() - { - return $this->username; - } - - /** - * Get the query part of the URL as a QueryString object - * - * @return QueryString - */ - public function getQuery() - { - return $this->query; - } - - /** - * Set the query part of the URL - * - * @param QueryString|string|array $query Query to set - * - * @return Url - */ - public function setQuery($query) - { - if (is_string($query)) { - $output = null; - parse_str($query, $output); - $this->query = new QueryString($output); - } elseif (is_array($query)) { - $this->query = new QueryString($query); - } elseif ($query instanceof QueryString) { - $this->query = $query; - } - - return $this; - } - - /** - * Get the fragment part of the URL - * - * @return null|string - */ - public function getFragment() - { - return $this->fragment; - } - - /** - * Set the fragment part of the URL - * - * @param string $fragment Fragment to set - * - * @return Url - */ - public function setFragment($fragment) - { - $this->fragment = $fragment; - - return $this; - } - - /** - * Check if this is an absolute URL - * - * @return bool - */ - public function isAbsolute() - { - return $this->scheme && $this->host; - } - - /** - * Combine the URL with another URL. Follows the rules specific in RFC 3986 section 5.4. - * - * @param string $url Relative URL to combine with - * @param bool $strictRfc3986 Set to true to use strict RFC 3986 compliance when merging paths. When first - * released, Guzzle used an incorrect algorithm for combining relative URL paths. In - * order to not break users, we introduced this flag to allow the merging of URLs based - * on strict RFC 3986 section 5.4.1. This means that "http://a.com/foo/baz" merged with - * "bar" would become "http://a.com/foo/bar". When this value is set to false, it would - * become "http://a.com/foo/baz/bar". - * @return Url - * @throws InvalidArgumentException - * @link http://tools.ietf.org/html/rfc3986#section-5.4 - */ - public function combine($url, $strictRfc3986 = false) - { - $url = self::factory($url); - - // Use the more absolute URL as the base URL - if (!$this->isAbsolute() && $url->isAbsolute()) { - $url = $url->combine($this); - } - - // Passing a URL with a scheme overrides everything - if ($buffer = $url->getScheme()) { - $this->scheme = $buffer; - $this->host = $url->getHost(); - $this->port = $url->getPort(); - $this->username = $url->getUsername(); - $this->password = $url->getPassword(); - $this->path = $url->getPath(); - $this->query = $url->getQuery(); - $this->fragment = $url->getFragment(); - return $this; - } - - // Setting a host overrides the entire rest of the URL - if ($buffer = $url->getHost()) { - $this->host = $buffer; - $this->port = $url->getPort(); - $this->username = $url->getUsername(); - $this->password = $url->getPassword(); - $this->path = $url->getPath(); - $this->query = $url->getQuery(); - $this->fragment = $url->getFragment(); - return $this; - } - - $path = $url->getPath(); - $query = $url->getQuery(); - - if (!$path) { - if (count($query)) { - $this->addQuery($query, $strictRfc3986); - } - } else { - if ($path[0] == '/') { - $this->path = $path; - } elseif ($strictRfc3986) { - $this->path .= '/../' . $path; - } else { - $this->path .= '/' . $path; - } - $this->normalizePath(); - $this->addQuery($query, $strictRfc3986); - } - - $this->fragment = $url->getFragment(); - - return $this; - } - - private function addQuery(QueryString $new, $strictRfc386) - { - if (!$strictRfc386) { - $new->merge($this->query); - } - - $this->query = $new; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json deleted file mode 100644 index 9384a5bf9..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Http/composer.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "guzzle/http", - "description": "HTTP libraries used by Guzzle", - "homepage": "http://guzzlephp.org/", - "keywords": ["http client", "http", "client", "Guzzle", "curl"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": "self.version", - "guzzle/parser": "self.version", - "guzzle/stream": "self.version" - }, - "suggest": { - "ext-curl": "*" - }, - "autoload": { - "psr-0": { "Guzzle\\Http": "" } - }, - "target-dir": "Guzzle/Http", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php deleted file mode 100644 index c6997734c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/Inflector.php +++ /dev/null @@ -1,38 +0,0 @@ - array(), - 'camel' => array() - ); - - /** @var int Max entries per cache */ - protected $maxCacheSize; - - /** @var InflectorInterface Decorated inflector */ - protected $decoratedInflector; - - /** - * @param InflectorInterface $inflector Inflector being decorated - * @param int $maxCacheSize Maximum number of cached items to hold per cache - */ - public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) - { - $this->decoratedInflector = $inflector; - $this->maxCacheSize = $maxCacheSize; - } - - public function snake($word) - { - if (!isset($this->cache['snake'][$word])) { - $this->pruneCache('snake'); - $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); - } - - return $this->cache['snake'][$word]; - } - - /** - * Converts strings from snake_case to upper CamelCase - * - * @param string $word Value to convert into upper CamelCase - * - * @return string - */ - public function camel($word) - { - if (!isset($this->cache['camel'][$word])) { - $this->pruneCache('camel'); - $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); - } - - return $this->cache['camel'][$word]; - } - - /** - * Prune one of the named caches by removing 20% of the cache if it is full - * - * @param string $cache Type of cache to prune - */ - protected function pruneCache($cache) - { - if (count($this->cache[$cache]) == $this->maxCacheSize) { - $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php deleted file mode 100644 index db37e4fe4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/PreComputedInflector.php +++ /dev/null @@ -1,59 +0,0 @@ - array(), - 'camel' => array() - ); - - /** @var InflectorInterface Decorated inflector */ - protected $decoratedInflector; - - /** - * @param InflectorInterface $inflector Inflector being decorated - * @param array $snake Hash of pre-computed camel to snake - * @param array $camel Hash of pre-computed snake to camel - * @param bool $mirror Mirror snake and camel reflections - */ - public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) - { - if ($mirror) { - $camel = array_merge(array_flip($snake), $camel); - $snake = array_merge(array_flip($camel), $snake); - } - - $this->decoratedInflector = $inflector; - $this->mapping = array( - 'snake' => $snake, - 'camel' => $camel - ); - } - - public function snake($word) - { - return isset($this->mapping['snake'][$word]) - ? $this->mapping['snake'][$word] - : $this->decoratedInflector->snake($word); - } - - /** - * Converts strings from snake_case to upper CamelCase - * - * @param string $word Value to convert into upper CamelCase - * - * @return string - */ - public function camel($word) - { - return isset($this->mapping['camel'][$word]) - ? $this->mapping['camel'][$word] - : $this->decoratedInflector->camel($word); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json deleted file mode 100644 index 93f9e7b72..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Inflection/composer.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "guzzle/inflection", - "description": "Guzzle inflection component", - "homepage": "http://guzzlephp.org/", - "keywords": ["inflection", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2" - }, - "autoload": { - "psr-0": { "Guzzle\\Inflection": "" } - }, - "target-dir": "Guzzle/Inflection", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php deleted file mode 100644 index 1b6bd7e53..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/AppendIterator.php +++ /dev/null @@ -1,19 +0,0 @@ -getArrayIterator()->append($iterator); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php deleted file mode 100644 index d76cdd439..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/ChunkedIterator.php +++ /dev/null @@ -1,56 +0,0 @@ -chunkSize = $chunkSize; - } - - public function rewind() - { - parent::rewind(); - $this->next(); - } - - public function next() - { - $this->chunk = array(); - for ($i = 0; $i < $this->chunkSize && parent::valid(); $i++) { - $this->chunk[] = parent::current(); - parent::next(); - } - } - - public function current() - { - return $this->chunk; - } - - public function valid() - { - return (bool) $this->chunk; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php deleted file mode 100644 index b103367b6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/FilterIterator.php +++ /dev/null @@ -1,36 +0,0 @@ -callback = $callback; - } - - public function accept() - { - return call_user_func($this->callback, $this->current()); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php deleted file mode 100644 index 7e586bda6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MapIterator.php +++ /dev/null @@ -1,34 +0,0 @@ -callback = $callback; - } - - public function current() - { - return call_user_func($this->callback, parent::current()); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php deleted file mode 100644 index de4ab0360..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/MethodProxyIterator.php +++ /dev/null @@ -1,27 +0,0 @@ -getInnerIterator(); - while ($i instanceof \OuterIterator) { - $i = $i->getInnerIterator(); - } - - return call_user_func_array(array($i, $name), $args); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md deleted file mode 100644 index 8bb7e08e2..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/README.md +++ /dev/null @@ -1,25 +0,0 @@ -Guzzle Iterator -=============== - -Provides useful Iterators and Iterator decorators - -- ChunkedIterator: Pulls out chunks from an inner iterator and yields the chunks as arrays -- FilterIterator: Used when PHP 5.4's CallbackFilterIterator is not available -- MapIterator: Maps values before yielding -- MethodProxyIterator: Proxies missing method calls to the innermost iterator - -### Installing via Composer - -```bash -# Install Composer -curl -sS https://getcomposer.org/installer | php - -# Add Guzzle as a dependency -php composer.phar require guzzle/iterator:~3.0 -``` - -After installing, you need to require Composer's autoloader: - -```php -require 'vendor/autoload.php'; -``` diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json deleted file mode 100644 index ee1737987..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Iterator/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/iterator", - "description": "Provides helpful iterators and iterator decorators", - "keywords": ["iterator", "guzzle"], - "homepage": "http://guzzlephp.org/", - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/common": ">=2.8.0" - }, - "autoload": { - "psr-0": { "Guzzle\\Iterator": "/" } - }, - "target-dir": "Guzzle/Iterator", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php deleted file mode 100644 index 7f6271bcb..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/AbstractLogAdapter.php +++ /dev/null @@ -1,16 +0,0 @@ -log; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php deleted file mode 100644 index a70fc8d42..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/ArrayLogAdapter.php +++ /dev/null @@ -1,34 +0,0 @@ -logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); - } - - /** - * Get logged entries - * - * @return array - */ - public function getLogs() - { - return $this->logs; - } - - /** - * Clears logged entries - */ - public function clearLogs() - { - $this->logs = array(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php deleted file mode 100644 index d4bb73f21..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/ClosureLogAdapter.php +++ /dev/null @@ -1,23 +0,0 @@ -log = $logObject; - } - - public function log($message, $priority = LOG_INFO, $extras = array()) - { - call_user_func($this->log, $message, $priority, $extras); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php deleted file mode 100644 index d7ac4ea7c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/LogAdapterInterface.php +++ /dev/null @@ -1,18 +0,0 @@ ->>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{curl_stderr}"; - const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}'; - - /** - * @var string Template used to format log messages - */ - protected $template; - - /** - * @param string $template Log message template - */ - public function __construct($template = self::DEFAULT_FORMAT) - { - $this->template = $template ?: self::DEFAULT_FORMAT; - } - - /** - * Set the template to use for logging - * - * @param string $template Log message template - * - * @return self - */ - public function setTemplate($template) - { - $this->template = $template; - - return $this; - } - - /** - * Returns a formatted message - * - * @param RequestInterface $request Request that was sent - * @param Response $response Response that was received - * @param CurlHandle $handle Curl handle associated with the message - * @param array $customData Associative array of custom template data - * - * @return string - */ - public function format( - RequestInterface $request, - Response $response = null, - CurlHandle $handle = null, - array $customData = array() - ) { - $cache = $customData; - - return preg_replace_callback( - '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', - function (array $matches) use ($request, $response, $handle, &$cache) { - - if (array_key_exists($matches[1], $cache)) { - return $cache[$matches[1]]; - } - - $result = ''; - switch ($matches[1]) { - case 'request': - $result = (string) $request; - break; - case 'response': - $result = (string) $response; - break; - case 'req_body': - $result = $request instanceof EntityEnclosingRequestInterface - ? (string) $request->getBody() : ''; - break; - case 'res_body': - $result = $response ? $response->getBody(true) : ''; - break; - case 'ts': - $result = gmdate('c'); - break; - case 'method': - $result = $request->getMethod(); - break; - case 'url': - $result = (string) $request->getUrl(); - break; - case 'resource': - $result = $request->getResource(); - break; - case 'protocol': - $result = 'HTTP'; - break; - case 'version': - $result = $request->getProtocolVersion(); - break; - case 'host': - $result = $request->getHost(); - break; - case 'hostname': - $result = gethostname(); - break; - case 'port': - $result = $request->getPort(); - break; - case 'code': - $result = $response ? $response->getStatusCode() : ''; - break; - case 'phrase': - $result = $response ? $response->getReasonPhrase() : ''; - break; - case 'connect_time': - $result = $handle && $handle->getInfo(CURLINFO_CONNECT_TIME) - ? $handle->getInfo(CURLINFO_CONNECT_TIME) - : ($response ? $response->getInfo('connect_time') : ''); - break; - case 'total_time': - $result = $handle && $handle->getInfo(CURLINFO_TOTAL_TIME) - ? $handle->getInfo(CURLINFO_TOTAL_TIME) - : ($response ? $response->getInfo('total_time') : ''); - break; - case 'curl_error': - $result = $handle ? $handle->getError() : ''; - break; - case 'curl_code': - $result = $handle ? $handle->getErrorNo() : ''; - break; - case 'curl_stderr': - $result = $handle ? $handle->getStderr() : ''; - break; - default: - if (strpos($matches[1], 'req_header_') === 0) { - $result = $request->getHeader(substr($matches[1], 11)); - } elseif ($response && strpos($matches[1], 'res_header_') === 0) { - $result = $response->getHeader(substr($matches[1], 11)); - } - } - - $cache[$matches[1]] = $result; - return $result; - }, - $this->template - ); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php deleted file mode 100644 index 6afe7b62a..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/MonologLogAdapter.php +++ /dev/null @@ -1,34 +0,0 @@ - Logger::DEBUG, - LOG_INFO => Logger::INFO, - LOG_WARNING => Logger::WARNING, - LOG_ERR => Logger::ERROR, - LOG_CRIT => Logger::CRITICAL, - LOG_ALERT => Logger::ALERT - ); - - public function __construct(Logger $logObject) - { - $this->log = $logObject; - } - - public function log($message, $priority = LOG_INFO, $extras = array()) - { - $this->log->addRecord(self::$mapping[$priority], $message, $extras); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php deleted file mode 100644 index 38a2b600d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/PsrLogAdapter.php +++ /dev/null @@ -1,36 +0,0 @@ - LogLevel::DEBUG, - LOG_INFO => LogLevel::INFO, - LOG_WARNING => LogLevel::WARNING, - LOG_ERR => LogLevel::ERROR, - LOG_CRIT => LogLevel::CRITICAL, - LOG_ALERT => LogLevel::ALERT - ); - - public function __construct(LoggerInterface $logObject) - { - $this->log = $logObject; - } - - public function log($message, $priority = LOG_INFO, $extras = array()) - { - $this->log->log(self::$mapping[$priority], $message, $extras); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php deleted file mode 100644 index 0ea8e3b1d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf1LogAdapter.php +++ /dev/null @@ -1,24 +0,0 @@ -log = $logObject; - Version::warn(__CLASS__ . ' is deprecated'); - } - - public function log($message, $priority = LOG_INFO, $extras = array()) - { - $this->log->log($message, $priority, $extras); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php b/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php deleted file mode 100644 index 863f6a1c4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/Zf2LogAdapter.php +++ /dev/null @@ -1,21 +0,0 @@ -log = $logObject; - } - - public function log($message, $priority = LOG_INFO, $extras = array()) - { - $this->log->log($priority, $message, $extras); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json deleted file mode 100644 index a8213e8b4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Log/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "guzzle/log", - "description": "Guzzle log adapter component", - "homepage": "http://guzzlephp.org/", - "keywords": ["log", "adapter", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2" - }, - "autoload": { - "psr-0": { "Guzzle\\Log": "" } - }, - "suggest": { - "guzzle/http": "self.version" - }, - "target-dir": "Guzzle/Log", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php deleted file mode 100644 index 4349eeb38..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php +++ /dev/null @@ -1,131 +0,0 @@ - 'Domain', - 'path' => 'Path', - 'max_age' => 'Max-Age', - 'expires' => 'Expires', - 'version' => 'Version', - 'secure' => 'Secure', - 'port' => 'Port', - 'discard' => 'Discard', - 'comment' => 'Comment', - 'comment_url' => 'Comment-Url', - 'http_only' => 'HttpOnly' - ); - - public function parseCookie($cookie, $host = null, $path = null, $decode = false) - { - // Explode the cookie string using a series of semicolons - $pieces = array_filter(array_map('trim', explode(';', $cookie))); - - // The name of the cookie (first kvp) must include an equal sign. - if (empty($pieces) || !strpos($pieces[0], '=')) { - return false; - } - - // Create the default return array - $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( - 'cookies' => array(), - 'data' => array(), - 'path' => null, - 'http_only' => false, - 'discard' => false, - 'domain' => $host - )); - $foundNonCookies = 0; - - // Add the cookie pieces into the parsed data array - foreach ($pieces as $part) { - - $cookieParts = explode('=', $part, 2); - $key = trim($cookieParts[0]); - - if (count($cookieParts) == 1) { - // Can be a single value (e.g. secure, httpOnly) - $value = true; - } else { - // Be sure to strip wrapping quotes - $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); - if ($decode) { - $value = urldecode($value); - } - } - - // Only check for non-cookies when cookies have been found - if (!empty($data['cookies'])) { - foreach (self::$cookieParts as $mapValue => $search) { - if (!strcasecmp($search, $key)) { - $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; - $foundNonCookies++; - continue 2; - } - } - } - - // If cookies have not yet been retrieved, or this value was not found in the pieces array, treat it as a - // cookie. IF non-cookies have been parsed, then this isn't a cookie, it's cookie data. Cookies then data. - $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; - } - - // Calculate the expires date - if (!$data['expires'] && $data['max_age']) { - $data['expires'] = time() + (int) $data['max_age']; - } - - // Check path attribute according RFC6265 http://tools.ietf.org/search/rfc6265#section-5.2.4 - // "If the attribute-value is empty or if the first character of the - // attribute-value is not %x2F ("/"): - // Let cookie-path be the default-path. - // Otherwise: - // Let cookie-path be the attribute-value." - if (!$data['path'] || substr($data['path'], 0, 1) !== '/') { - $data['path'] = $this->getDefaultPath($path); - } - - return $data; - } - - /** - * Get default cookie path according to RFC 6265 - * http://tools.ietf.org/search/rfc6265#section-5.1.4 Paths and Path-Match - * - * @param string $path Request uri-path - * - * @return string - */ - protected function getDefaultPath($path) { - // "The user agent MUST use an algorithm equivalent to the following algorithm - // to compute the default-path of a cookie:" - - // "2. If the uri-path is empty or if the first character of the uri-path is not - // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. - if (empty($path) || substr($path, 0, 1) !== '/') { - return '/'; - } - - // "3. If the uri-path contains no more than one %x2F ("/") character, output - // %x2F ("/") and skip the remaining step." - if ($path === "/") { - return $path; - } - - $rightSlashPos = strrpos($path, '/'); - if ($rightSlashPos === 0) { - return "/"; - } - - // "4. Output the characters of the uri-path from the first character up to, - // but not including, the right-most %x2F ("/")." - return substr($path, 0, $rightSlashPos); - - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php deleted file mode 100644 index d21ffe21c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php +++ /dev/null @@ -1,33 +0,0 @@ - $requestUrl, - 'scheme' => 'http' - ); - - // Check for the Host header - if (isset($parts['headers']['Host'])) { - $urlParts['host'] = $parts['headers']['Host']; - } elseif (isset($parts['headers']['host'])) { - $urlParts['host'] = $parts['headers']['host']; - } else { - $urlParts['host'] = null; - } - - if (false === strpos($urlParts['host'], ':')) { - $urlParts['port'] = ''; - } else { - $hostParts = explode(':', $urlParts['host']); - $urlParts['host'] = trim($hostParts[0]); - $urlParts['port'] = (int) trim($hostParts[1]); - if ($urlParts['port'] == 443) { - $urlParts['scheme'] = 'https'; - } - } - - // Check if a query is present - $path = $urlParts['path']; - $qpos = strpos($path, '?'); - if ($qpos) { - $urlParts['query'] = substr($path, $qpos + 1); - $urlParts['path'] = substr($path, 0, $qpos); - } else { - $urlParts['query'] = ''; - } - - return $urlParts; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php deleted file mode 100644 index 104740068..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php +++ /dev/null @@ -1,110 +0,0 @@ -parseMessage($message); - - // Parse the protocol and protocol version - if (isset($parts['start_line'][2])) { - $startParts = explode('/', $parts['start_line'][2]); - $protocol = strtoupper($startParts[0]); - $version = isset($startParts[1]) ? $startParts[1] : '1.1'; - } else { - $protocol = 'HTTP'; - $version = '1.1'; - } - - $parsed = array( - 'method' => strtoupper($parts['start_line'][0]), - 'protocol' => $protocol, - 'version' => $version, - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ); - - $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed); - - return $parsed; - } - - public function parseResponse($message) - { - if (!$message) { - return false; - } - - $parts = $this->parseMessage($message); - list($protocol, $version) = explode('/', trim($parts['start_line'][0])); - - return array( - 'protocol' => $protocol, - 'version' => $version, - 'code' => $parts['start_line'][1], - 'reason_phrase' => isset($parts['start_line'][2]) ? $parts['start_line'][2] : '', - 'headers' => $parts['headers'], - 'body' => $parts['body'] - ); - } - - /** - * Parse a message into parts - * - * @param string $message Message to parse - * - * @return array - */ - protected function parseMessage($message) - { - $startLine = null; - $headers = array(); - $body = ''; - - // Iterate over each line in the message, accounting for line endings - $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); - for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { - - $line = $lines[$i]; - - // If two line breaks were encountered, then this is the end of body - if (empty($line)) { - if ($i < $totalLines - 1) { - $body = implode('', array_slice($lines, $i + 2)); - } - break; - } - - // Parse message headers - if (!$startLine) { - $startLine = explode(' ', $line, 3); - } elseif (strpos($line, ':')) { - $parts = explode(':', $line, 2); - $key = trim($parts[0]); - $value = isset($parts[1]) ? trim($parts[1]) : ''; - if (!isset($headers[$key])) { - $headers[$key] = $value; - } elseif (!is_array($headers[$key])) { - $headers[$key] = array($headers[$key], $value); - } else { - $headers[$key][] = $value; - } - } - } - - return array( - 'start_line' => $startLine, - 'headers' => $headers, - 'body' => $body - ); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php deleted file mode 100644 index cc448088d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - $parts->requestMethod, - 'protocol' => 'HTTP', - 'version' => number_format($parts->httpVersion, 1), - 'headers' => $parts->headers, - 'body' => $parts->body - ); - - $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); - - return $parsed; - } - - public function parseResponse($message) - { - if (!$message) { - return false; - } - - $parts = http_parse_message($message); - - return array( - 'protocol' => 'HTTP', - 'version' => number_format($parts->httpVersion, 1), - 'code' => $parts->responseCode, - 'reason_phrase' => $parts->responseStatus, - 'headers' => $parts->headers, - 'body' => $parts->body - ); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php deleted file mode 100644 index f8386831c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php +++ /dev/null @@ -1,75 +0,0 @@ - 'Guzzle\\Parser\\Message\\MessageParser', - 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', - 'url' => 'Guzzle\\Parser\\Url\\UrlParser', - 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', - ); - - /** - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new static; - } - - return self::$instance; - } - - public function __construct() - { - // Use the PECL URI template parser if available - if (extension_loaded('uri_template')) { - $this->mapping['uri_template'] = 'Guzzle\\Parser\\UriTemplate\\PeclUriTemplate'; - } - } - - /** - * Get a parser by name from an instance - * - * @param string $name Name of the parser to retrieve - * - * @return mixed|null - */ - public function getParser($name) - { - if (!isset($this->instances[$name])) { - if (!isset($this->mapping[$name])) { - return null; - } - $class = $this->mapping[$name]; - $this->instances[$name] = new $class(); - } - - return $this->instances[$name]; - } - - /** - * Register a custom parser by name with the register - * - * @param string $name Name or handle of the parser to register - * @param mixed $parser Instantiated parser to register - */ - public function registerParser($name, $parser) - { - $this->instances[$name] = $parser; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php deleted file mode 100644 index b0764e837..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/PeclUriTemplate.php +++ /dev/null @@ -1,26 +0,0 @@ - true, '#' => true, '.' => true, '/' => true, ';' => true, '?' => true, '&' => true - ); - - /** @var array Delimiters */ - private static $delims = array( - ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' - ); - - /** @var array Percent encoded delimiters */ - private static $delimsPct = array( - '%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', - '%3B', '%3D' - ); - - public function expand($template, array $variables) - { - if ($this->regex == self::DEFAULT_PATTERN && false === strpos($template, '{')) { - return $template; - } - - $this->template = $template; - $this->variables = $variables; - - return preg_replace_callback($this->regex, array($this, 'expandMatch'), $this->template); - } - - /** - * Set the regex patten used to expand URI templates - * - * @param string $regexPattern - */ - public function setRegex($regexPattern) - { - $this->regex = $regexPattern; - } - - /** - * Parse an expression into parts - * - * @param string $expression Expression to parse - * - * @return array Returns an associative array of parts - */ - private function parseExpression($expression) - { - // Check for URI operators - $operator = ''; - - if (isset(self::$operatorHash[$expression[0]])) { - $operator = $expression[0]; - $expression = substr($expression, 1); - } - - $values = explode(',', $expression); - foreach ($values as &$value) { - $value = trim($value); - $varspec = array(); - $substrPos = strpos($value, ':'); - if ($substrPos) { - $varspec['value'] = substr($value, 0, $substrPos); - $varspec['modifier'] = ':'; - $varspec['position'] = (int) substr($value, $substrPos + 1); - } elseif (substr($value, -1) == '*') { - $varspec['modifier'] = '*'; - $varspec['value'] = substr($value, 0, -1); - } else { - $varspec['value'] = (string) $value; - $varspec['modifier'] = ''; - } - $value = $varspec; - } - - return array( - 'operator' => $operator, - 'values' => $values - ); - } - - /** - * Process an expansion - * - * @param array $matches Matches met in the preg_replace_callback - * - * @return string Returns the replacement string - */ - private function expandMatch(array $matches) - { - static $rfc1738to3986 = array( - '+' => '%20', - '%7e' => '~' - ); - - $parsed = self::parseExpression($matches[1]); - $replacements = array(); - - $prefix = $parsed['operator']; - $joiner = $parsed['operator']; - $useQueryString = false; - if ($parsed['operator'] == '?') { - $joiner = '&'; - $useQueryString = true; - } elseif ($parsed['operator'] == '&') { - $useQueryString = true; - } elseif ($parsed['operator'] == '#') { - $joiner = ','; - } elseif ($parsed['operator'] == ';') { - $useQueryString = true; - } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { - $joiner = ','; - $prefix = ''; - } - - foreach ($parsed['values'] as $value) { - - if (!array_key_exists($value['value'], $this->variables) || $this->variables[$value['value']] === null) { - continue; - } - - $variable = $this->variables[$value['value']]; - $actuallyUseQueryString = $useQueryString; - $expanded = ''; - - if (is_array($variable)) { - - $isAssoc = $this->isAssoc($variable); - $kvp = array(); - foreach ($variable as $key => $var) { - - if ($isAssoc) { - $key = rawurlencode($key); - $isNestedArray = is_array($var); - } else { - $isNestedArray = false; - } - - if (!$isNestedArray) { - $var = rawurlencode($var); - if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { - $var = $this->decodeReserved($var); - } - } - - if ($value['modifier'] == '*') { - if ($isAssoc) { - if ($isNestedArray) { - // Nested arrays must allow for deeply nested structures - $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); - } else { - $var = $key . '=' . $var; - } - } elseif ($key > 0 && $actuallyUseQueryString) { - $var = $value['value'] . '=' . $var; - } - } - - $kvp[$key] = $var; - } - - if (empty($variable)) { - $actuallyUseQueryString = false; - } elseif ($value['modifier'] == '*') { - $expanded = implode($joiner, $kvp); - if ($isAssoc) { - // Don't prepend the value name when using the explode modifier with an associative array - $actuallyUseQueryString = false; - } - } else { - if ($isAssoc) { - // When an associative array is encountered and the explode modifier is not set, then the - // result must be a comma separated list of keys followed by their respective values. - foreach ($kvp as $k => &$v) { - $v = $k . ',' . $v; - } - } - $expanded = implode(',', $kvp); - } - - } else { - if ($value['modifier'] == ':') { - $variable = substr($variable, 0, $value['position']); - } - $expanded = rawurlencode($variable); - if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { - $expanded = $this->decodeReserved($expanded); - } - } - - if ($actuallyUseQueryString) { - if (!$expanded && $joiner != '&') { - $expanded = $value['value']; - } else { - $expanded = $value['value'] . '=' . $expanded; - } - } - - $replacements[] = $expanded; - } - - $ret = implode($joiner, $replacements); - if ($ret && $prefix) { - return $prefix . $ret; - } - - return $ret; - } - - /** - * Determines if an array is associative - * - * @param array $array Array to check - * - * @return bool - */ - private function isAssoc(array $array) - { - return (bool) count(array_filter(array_keys($array), 'is_string')); - } - - /** - * Removes percent encoding on reserved characters (used with + and # modifiers) - * - * @param string $string String to fix - * - * @return string - */ - private function decodeReserved($string) - { - return str_replace(self::$delimsPct, self::$delims, $string); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php deleted file mode 100644 index c81d51548..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -utf8 = $utf8; - } - - public function parseUrl($url) - { - Version::warn(__CLASS__ . ' is deprecated. Just use parse_url()'); - - static $defaults = array('scheme' => null, 'host' => null, 'path' => null, 'port' => null, 'query' => null, - 'user' => null, 'pass' => null, 'fragment' => null); - - $parts = parse_url($url); - - // Need to handle query parsing specially for UTF-8 requirements - if ($this->utf8 && isset($parts['query'])) { - $queryPos = strpos($url, '?'); - if (isset($parts['fragment'])) { - $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); - } else { - $parts['query'] = substr($url, $queryPos + 1); - } - } - - return $parts + $defaults; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php deleted file mode 100644 index 89ac4b307..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -=5.3.2" - }, - "autoload": { - "psr-0": { "Guzzle\\Parser": "" } - }, - "target-dir": "Guzzle/Parser", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php deleted file mode 100644 index ae5941873..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/AsyncPlugin.php +++ /dev/null @@ -1,84 +0,0 @@ - 'onBeforeSend', - 'request.exception' => 'onRequestTimeout', - 'request.sent' => 'onRequestSent', - 'curl.callback.progress' => 'onCurlProgress' - ); - } - - /** - * Event used to ensure that progress callback are emitted from the curl handle's request mediator. - * - * @param Event $event - */ - public function onBeforeSend(Event $event) - { - // Ensure that progress callbacks are dispatched - $event['request']->getCurlOptions()->set('progress', true); - } - - /** - * Event emitted when a curl progress function is called. When the amount of data uploaded == the amount of data to - * upload OR any bytes have been downloaded, then time the request out after 1ms because we're done with - * transmitting the request, and tell curl not download a body. - * - * @param Event $event - */ - public function onCurlProgress(Event $event) - { - if ($event['handle'] && - ($event['downloaded'] || (isset($event['uploaded']) && $event['upload_size'] === $event['uploaded'])) - ) { - // Timeout after 1ms - curl_setopt($event['handle'], CURLOPT_TIMEOUT_MS, 1); - // Even if the response is quick, tell curl not to download the body. - // - Note that we can only perform this shortcut if the request transmitted a body so as to ensure that the - // request method is not converted to a HEAD request before the request was sent via curl. - if ($event['uploaded']) { - curl_setopt($event['handle'], CURLOPT_NOBODY, true); - } - } - } - - /** - * Event emitted when a curl exception occurs. Ignore the exception and set a mock response. - * - * @param Event $event - */ - public function onRequestTimeout(Event $event) - { - if ($event['exception'] instanceof CurlException) { - $event['request']->setResponse(new Response(200, array( - 'X-Guzzle-Async' => 'Did not wait for the response' - ))); - } - } - - /** - * Event emitted when a request completes because it took less than 1ms. Add an X-Guzzle-Async header to notify the - * caller that there is no body in the message. - * - * @param Event $event - */ - public function onRequestSent(Event $event) - { - // Let the caller know this was meant to be async - $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json deleted file mode 100644 index dc3fc5bf8..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Async/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-async", - "description": "Guzzle async request plugin", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Async": "" } - }, - "target-dir": "Guzzle/Plugin/Async", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php deleted file mode 100644 index 0a8598345..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractBackoffStrategy.php +++ /dev/null @@ -1,91 +0,0 @@ -next = $next; - } - - /** - * Get the next backoff strategy in the chain - * - * @return AbstractBackoffStrategy|null - */ - public function getNext() - { - return $this->next; - } - - public function getBackoffPeriod( - $retries, - RequestInterface $request, - Response $response = null, - HttpException $e = null - ) { - $delay = $this->getDelay($retries, $request, $response, $e); - if ($delay === false) { - // The strategy knows that this must not be retried - return false; - } elseif ($delay === null) { - // If the strategy is deferring a decision and the next strategy will not make a decision then return false - return !$this->next || !$this->next->makesDecision() - ? false - : $this->next->getBackoffPeriod($retries, $request, $response, $e); - } elseif ($delay === true) { - // if the strategy knows that it must retry but is deferring to the next to determine the delay - if (!$this->next) { - return 0; - } else { - $next = $this->next; - while ($next->makesDecision() && $next->getNext()) { - $next = $next->getNext(); - } - return !$next->makesDecision() ? $next->getBackoffPeriod($retries, $request, $response, $e) : 0; - } - } else { - return $delay; - } - } - - /** - * Check if the strategy does filtering and makes decisions on whether or not to retry. - * - * Strategies that return false will never retry if all of the previous strategies in a chain defer on a backoff - * decision. - * - * @return bool - */ - abstract public function makesDecision(); - - /** - * Implement the concrete strategy - * - * @param int $retries Number of retries of the request - * @param RequestInterface $request Request that was sent - * @param Response $response Response that was received. Note that there may not be a response - * @param HttpException $e Exception that was encountered if any - * - * @return bool|int|null Returns false to not retry or the number of seconds to delay between retries. Return true - * or null to defer to the next strategy if available, and if not, return 0. - */ - abstract protected function getDelay( - $retries, - RequestInterface $request, - Response $response = null, - HttpException $e = null - ); -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php deleted file mode 100644 index 6ebee6c1a..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/AbstractErrorCodeBackoffStrategy.php +++ /dev/null @@ -1,40 +0,0 @@ -errorCodes = array_fill_keys($codes ?: static::$defaultErrorCodes, 1); - $this->next = $next; - } - - /** - * Get the default failure codes to retry - * - * @return array - */ - public static function getDefaultFailureCodes() - { - return static::$defaultErrorCodes; - } - - public function makesDecision() - { - return true; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php deleted file mode 100644 index ec54c289e..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffLogger.php +++ /dev/null @@ -1,76 +0,0 @@ -logger = $logger; - $this->formatter = $formatter ?: new MessageFormatter(self::DEFAULT_FORMAT); - } - - public static function getSubscribedEvents() - { - return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); - } - - /** - * Set the template to use for logging - * - * @param string $template Log message template - * - * @return self - */ - public function setTemplate($template) - { - $this->formatter->setTemplate($template); - - return $this; - } - - /** - * Called when a request is being retried - * - * @param Event $event Event emitted - */ - public function onRequestRetry(Event $event) - { - $this->logger->log($this->formatter->format( - $event['request'], - $event['response'], - $event['handle'], - array( - 'retries' => $event['retries'], - 'delay' => $event['delay'] - ) - )); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php deleted file mode 100644 index 99ace0538..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffPlugin.php +++ /dev/null @@ -1,126 +0,0 @@ -strategy = $strategy; - } - - /** - * Retrieve a basic truncated exponential backoff plugin that will retry HTTP errors and cURL errors - * - * @param int $maxRetries Maximum number of retries - * @param array $httpCodes HTTP response codes to retry - * @param array $curlCodes cURL error codes to retry - * - * @return self - */ - public static function getExponentialBackoff( - $maxRetries = 3, - array $httpCodes = null, - array $curlCodes = null - ) { - return new self(new TruncatedBackoffStrategy($maxRetries, - new HttpBackoffStrategy($httpCodes, - new CurlBackoffStrategy($curlCodes, - new ExponentialBackoffStrategy() - ) - ) - )); - } - - public static function getAllEvents() - { - return array(self::RETRY_EVENT); - } - - public static function getSubscribedEvents() - { - return array( - 'request.sent' => 'onRequestSent', - 'request.exception' => 'onRequestSent', - CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' - ); - } - - /** - * Called when a request has been sent and isn't finished processing - * - * @param Event $event - */ - public function onRequestSent(Event $event) - { - $request = $event['request']; - $response = $event['response']; - $exception = $event['exception']; - - $params = $request->getParams(); - $retries = (int) $params->get(self::RETRY_PARAM); - $delay = $this->strategy->getBackoffPeriod($retries, $request, $response, $exception); - - if ($delay !== false) { - // Calculate how long to wait until the request should be retried - $params->set(self::RETRY_PARAM, ++$retries) - ->set(self::DELAY_PARAM, microtime(true) + $delay); - // Send the request again - $request->setState(RequestInterface::STATE_TRANSFER); - $this->dispatch(self::RETRY_EVENT, array( - 'request' => $request, - 'response' => $response, - 'handle' => ($exception && $exception instanceof CurlException) ? $exception->getCurlHandle() : null, - 'retries' => $retries, - 'delay' => $delay - )); - } - } - - /** - * Called when a request is polling in the curl multi object - * - * @param Event $event - */ - public function onRequestPoll(Event $event) - { - $request = $event['request']; - $delay = $request->getParams()->get(self::DELAY_PARAM); - - // If the duration of the delay has passed, retry the request using the pool - if (null !== $delay && microtime(true) >= $delay) { - // Remove the request from the pool and then add it back again. This is required for cURL to know that we - // want to retry sending the easy handle. - $request->getParams()->remove(self::DELAY_PARAM); - // Rewind the request body if possible - if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()) { - $request->getBody()->seek(0); - } - $multi = $event['curl_multi']; - $multi->remove($request); - $multi->add($request); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php deleted file mode 100644 index 4e590dbe0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/BackoffStrategyInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -callback = $callback; - $this->decision = (bool) $decision; - $this->next = $next; - } - - public function makesDecision() - { - return $this->decision; - } - - protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) - { - return call_user_func($this->callback, $retries, $request, $response, $e); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php deleted file mode 100644 index 061d2a407..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ConstantBackoffStrategy.php +++ /dev/null @@ -1,34 +0,0 @@ -delay = $delay; - } - - public function makesDecision() - { - return false; - } - - protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) - { - return $this->delay; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php deleted file mode 100644 index a584ed4a2..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/CurlBackoffStrategy.php +++ /dev/null @@ -1,28 +0,0 @@ -errorCodes[$e->getErrorNo()]) ? true : null; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php deleted file mode 100644 index fb2912d50..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ExponentialBackoffStrategy.php +++ /dev/null @@ -1,25 +0,0 @@ -isSuccessful()) { - return false; - } else { - return isset($this->errorCodes[$response->getStatusCode()]) ? true : null; - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php deleted file mode 100644 index b35e8a490..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/LinearBackoffStrategy.php +++ /dev/null @@ -1,36 +0,0 @@ -step = $step; - } - - public function makesDecision() - { - return false; - } - - protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) - { - return $retries * $this->step; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php deleted file mode 100644 index 4fd73fedf..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/ReasonPhraseBackoffStrategy.php +++ /dev/null @@ -1,25 +0,0 @@ -errorCodes[$response->getReasonPhrase()]) ? true : null; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php deleted file mode 100644 index 3608f3584..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/TruncatedBackoffStrategy.php +++ /dev/null @@ -1,36 +0,0 @@ -max = $maxRetries; - $this->next = $next; - } - - public function makesDecision() - { - return true; - } - - protected function getDelay($retries, RequestInterface $request, Response $response = null, HttpException $e = null) - { - return $retries < $this->max ? null : false; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json deleted file mode 100644 index 91c122cb4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Backoff/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "guzzle/plugin-backoff", - "description": "Guzzle backoff retry plugins", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version", - "guzzle/log": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Backoff": "" } - }, - "target-dir": "Guzzle/Plugin/Backoff", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php deleted file mode 100644 index 7790f8844..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheKeyProviderInterface.php +++ /dev/null @@ -1,11 +0,0 @@ - new DefaultCacheStorage($options)); - } elseif ($options instanceof CacheStorageInterface) { - $options = array('storage' => $options); - } elseif ($options) { - $options = array('storage' => new DefaultCacheStorage(CacheAdapterFactory::fromCache($options))); - } elseif (!class_exists('Doctrine\Common\Cache\ArrayCache')) { - // @codeCoverageIgnoreStart - throw new InvalidArgumentException('No cache was provided and Doctrine is not installed'); - // @codeCoverageIgnoreEnd - } - } - - $this->autoPurge = isset($options['auto_purge']) ? $options['auto_purge'] : false; - - // Add a cache storage if a cache adapter was provided - $this->storage = isset($options['storage']) - ? $options['storage'] - : new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); - - if (!isset($options['can_cache'])) { - $this->canCache = new DefaultCanCacheStrategy(); - } else { - $this->canCache = is_callable($options['can_cache']) - ? new CallbackCanCacheStrategy($options['can_cache']) - : $options['can_cache']; - } - - // Use the provided revalidation strategy or the default - $this->revalidation = isset($options['revalidation']) - ? $options['revalidation'] - : new DefaultRevalidation($this->storage, $this->canCache); - } - - public static function getSubscribedEvents() - { - return array( - 'request.before_send' => array('onRequestBeforeSend', -255), - 'request.sent' => array('onRequestSent', 255), - 'request.error' => array('onRequestError', 0), - 'request.exception' => array('onRequestException', 0), - ); - } - - /** - * Check if a response in cache will satisfy the request before sending - * - * @param Event $event - */ - public function onRequestBeforeSend(Event $event) - { - $request = $event['request']; - $request->addHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); - - if (!$this->canCache->canCacheRequest($request)) { - switch ($request->getMethod()) { - case 'PURGE': - $this->purge($request); - $request->setResponse(new Response(200, array(), 'purged')); - break; - case 'PUT': - case 'POST': - case 'DELETE': - case 'PATCH': - if ($this->autoPurge) { - $this->purge($request); - } - } - return; - } - - if ($response = $this->storage->fetch($request)) { - $params = $request->getParams(); - $params['cache.lookup'] = true; - $response->setHeader( - 'Age', - time() - strtotime($response->getDate() ? : $response->getLastModified() ?: 'now') - ); - // Validate that the response satisfies the request - if ($this->canResponseSatisfyRequest($request, $response)) { - if (!isset($params['cache.hit'])) { - $params['cache.hit'] = true; - } - $request->setResponse($response); - } - } - } - - /** - * If possible, store a response in cache after sending - * - * @param Event $event - */ - public function onRequestSent(Event $event) - { - $request = $event['request']; - $response = $event['response']; - - if ($request->getParams()->get('cache.hit') === null && - $this->canCache->canCacheRequest($request) && - $this->canCache->canCacheResponse($response) - ) { - $this->storage->cache($request, $response); - } - - $this->addResponseHeaders($request, $response); - } - - /** - * If possible, return a cache response on an error - * - * @param Event $event - */ - public function onRequestError(Event $event) - { - $request = $event['request']; - - if (!$this->canCache->canCacheRequest($request)) { - return; - } - - if ($response = $this->storage->fetch($request)) { - $response->setHeader( - 'Age', - time() - strtotime($response->getLastModified() ? : $response->getDate() ?: 'now') - ); - - if ($this->canResponseSatisfyFailedRequest($request, $response)) { - $request->getParams()->set('cache.hit', 'error'); - $this->addResponseHeaders($request, $response); - $event['response'] = $response; - $event->stopPropagation(); - } - } - } - - /** - * If possible, set a cache response on a cURL exception - * - * @param Event $event - * - * @return null - */ - public function onRequestException(Event $event) - { - if (!$event['exception'] instanceof CurlException) { - return; - } - - $request = $event['request']; - if (!$this->canCache->canCacheRequest($request)) { - return; - } - - if ($response = $this->storage->fetch($request)) { - $response->setHeader('Age', time() - strtotime($response->getDate() ? : 'now')); - if (!$this->canResponseSatisfyFailedRequest($request, $response)) { - return; - } - $request->getParams()->set('cache.hit', 'error'); - $request->setResponse($response); - $this->addResponseHeaders($request, $response); - $event->stopPropagation(); - } - } - - /** - * Check if a cache response satisfies a request's caching constraints - * - * @param RequestInterface $request Request to validate - * @param Response $response Response to validate - * - * @return bool - */ - public function canResponseSatisfyRequest(RequestInterface $request, Response $response) - { - $responseAge = $response->calculateAge(); - $reqc = $request->getHeader('Cache-Control'); - $resc = $response->getHeader('Cache-Control'); - - // Check the request's max-age header against the age of the response - if ($reqc && $reqc->hasDirective('max-age') && - $responseAge > $reqc->getDirective('max-age')) { - return false; - } - - // Check the response's max-age header - if ($response->isFresh() === false) { - $maxStale = $reqc ? $reqc->getDirective('max-stale') : null; - if (null !== $maxStale) { - if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { - return false; - } - } elseif ($resc && $resc->hasDirective('max-age') - && $responseAge > $resc->getDirective('max-age') - ) { - return false; - } - } - - if ($this->revalidation->shouldRevalidate($request, $response)) { - try { - return $this->revalidation->revalidate($request, $response); - } catch (CurlException $e) { - $request->getParams()->set('cache.hit', 'error'); - return $this->canResponseSatisfyFailedRequest($request, $response); - } - } - - return true; - } - - /** - * Check if a cache response satisfies a failed request's caching constraints - * - * @param RequestInterface $request Request to validate - * @param Response $response Response to validate - * - * @return bool - */ - public function canResponseSatisfyFailedRequest(RequestInterface $request, Response $response) - { - $reqc = $request->getHeader('Cache-Control'); - $resc = $response->getHeader('Cache-Control'); - $requestStaleIfError = $reqc ? $reqc->getDirective('stale-if-error') : null; - $responseStaleIfError = $resc ? $resc->getDirective('stale-if-error') : null; - - if (!$requestStaleIfError && !$responseStaleIfError) { - return false; - } - - if (is_numeric($requestStaleIfError) && $response->getAge() - $response->getMaxAge() > $requestStaleIfError) { - return false; - } - - if (is_numeric($responseStaleIfError) && $response->getAge() - $response->getMaxAge() > $responseStaleIfError) { - return false; - } - - return true; - } - - /** - * Purge all cache entries for a given URL - * - * @param string $url URL to purge - */ - public function purge($url) - { - // BC compatibility with previous version that accepted a Request object - $url = $url instanceof RequestInterface ? $url->getUrl() : $url; - $this->storage->purge($url); - } - - /** - * Add the plugin's headers to a response - * - * @param RequestInterface $request Request - * @param Response $response Response to add headers to - */ - protected function addResponseHeaders(RequestInterface $request, Response $response) - { - $params = $request->getParams(); - $response->setHeader('Via', sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION)); - - $lookup = ($params['cache.lookup'] === true ? 'HIT' : 'MISS') . ' from GuzzleCache'; - if ($header = $response->getHeader('X-Cache-Lookup')) { - // Don't add duplicates - $values = $header->toArray(); - $values[] = $lookup; - $response->setHeader('X-Cache-Lookup', array_unique($values)); - } else { - $response->setHeader('X-Cache-Lookup', $lookup); - } - - if ($params['cache.hit'] === true) { - $xcache = 'HIT from GuzzleCache'; - } elseif ($params['cache.hit'] == 'error') { - $xcache = 'HIT_ERROR from GuzzleCache'; - } else { - $xcache = 'MISS from GuzzleCache'; - } - - if ($header = $response->getHeader('X-Cache')) { - // Don't add duplicates - $values = $header->toArray(); - $values[] = $xcache; - $response->setHeader('X-Cache', array_unique($values)); - } else { - $response->setHeader('X-Cache', $xcache); - } - - if ($response->isFresh() === false) { - $response->addHeader('Warning', sprintf('110 GuzzleCache/%s "Response is stale"', Version::VERSION)); - if ($params['cache.hit'] === 'error') { - $response->addHeader('Warning', sprintf('111 GuzzleCache/%s "Revalidation failed"', Version::VERSION)); - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php deleted file mode 100644 index f3d915458..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CacheStorageInterface.php +++ /dev/null @@ -1,43 +0,0 @@ -requestCallback = $requestCallback; - $this->responseCallback = $responseCallback; - } - - public function canCacheRequest(RequestInterface $request) - { - return $this->requestCallback - ? call_user_func($this->requestCallback, $request) - : parent::canCacheRequest($request); - } - - public function canCacheResponse(Response $response) - { - return $this->responseCallback - ? call_user_func($this->responseCallback, $response) - : parent::canCacheResponse($response); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php deleted file mode 100644 index 6e01a8e74..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/CanCacheStrategyInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -getParams()->get(self::CACHE_KEY); - - if (!$key) { - - $cloned = clone $request; - $cloned->removeHeader('Cache-Control'); - - // Check to see how and if the key should be filtered - foreach (explode(';', $request->getParams()->get(self::CACHE_KEY_FILTER)) as $part) { - $pieces = array_map('trim', explode('=', $part)); - if (isset($pieces[1])) { - foreach (array_map('trim', explode(',', $pieces[1])) as $remove) { - if ($pieces[0] == 'header') { - $cloned->removeHeader($remove); - } elseif ($pieces[0] == 'query') { - $cloned->getQuery()->remove($remove); - } - } - } - } - - $raw = (string) $cloned; - $key = 'GZ' . md5($raw); - $request->getParams()->set(self::CACHE_KEY, $key)->set(self::CACHE_KEY_RAW, $raw); - } - - return $key; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php deleted file mode 100644 index 555c9b79c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCacheStorage.php +++ /dev/null @@ -1,251 +0,0 @@ -cache = CacheAdapterFactory::fromCache($cache); - $this->defaultTtl = $defaultTtl; - $this->keyPrefix = $keyPrefix; - } - - public function cache(RequestInterface $request, Response $response) - { - $currentTime = time(); - $ttl = $request->getParams()->get('cache.override_ttl') ?: $response->getMaxAge() ?: $this->defaultTtl; - - if ($cacheControl = $response->getHeader('Cache-Control')) { - $stale = $cacheControl->getDirective('stale-if-error'); - $ttl += $stale == true ? $ttl : $stale; - } - - // Determine which manifest key should be used - $key = $this->getCacheKey($request); - $persistedRequest = $this->persistHeaders($request); - $entries = array(); - - if ($manifest = $this->cache->fetch($key)) { - // Determine which cache entries should still be in the cache - $vary = $response->getVary(); - foreach (unserialize($manifest) as $entry) { - // Check if the entry is expired - if ($entry[4] < $currentTime) { - continue; - } - $entry[1]['vary'] = isset($entry[1]['vary']) ? $entry[1]['vary'] : ''; - if ($vary != $entry[1]['vary'] || !$this->requestsMatch($vary, $entry[0], $persistedRequest)) { - $entries[] = $entry; - } - } - } - - // Persist the response body if needed - $bodyDigest = null; - if ($response->getBody() && $response->getBody()->getContentLength() > 0) { - $bodyDigest = $this->getBodyKey($request->getUrl(), $response->getBody()); - $this->cache->save($bodyDigest, (string) $response->getBody(), $ttl); - } - - array_unshift($entries, array( - $persistedRequest, - $this->persistHeaders($response), - $response->getStatusCode(), - $bodyDigest, - $currentTime + $ttl - )); - - $this->cache->save($key, serialize($entries)); - } - - public function delete(RequestInterface $request) - { - $key = $this->getCacheKey($request); - if ($entries = $this->cache->fetch($key)) { - // Delete each cached body - foreach (unserialize($entries) as $entry) { - if ($entry[3]) { - $this->cache->delete($entry[3]); - } - } - $this->cache->delete($key); - } - } - - public function purge($url) - { - foreach (array('GET', 'HEAD', 'POST', 'PUT', 'DELETE') as $method) { - $this->delete(new Request($method, $url)); - } - } - - public function fetch(RequestInterface $request) - { - $key = $this->getCacheKey($request); - if (!($entries = $this->cache->fetch($key))) { - return null; - } - - $match = null; - $headers = $this->persistHeaders($request); - $entries = unserialize($entries); - foreach ($entries as $index => $entry) { - if ($this->requestsMatch(isset($entry[1]['vary']) ? $entry[1]['vary'] : '', $headers, $entry[0])) { - $match = $entry; - break; - } - } - - if (!$match) { - return null; - } - - // Ensure that the response is not expired - $response = null; - if ($match[4] < time()) { - $response = -1; - } else { - $response = new Response($match[2], $match[1]); - if ($match[3]) { - if ($body = $this->cache->fetch($match[3])) { - $response->setBody($body); - } else { - // The response is not valid because the body was somehow deleted - $response = -1; - } - } - } - - if ($response === -1) { - // Remove the entry from the metadata and update the cache - unset($entries[$index]); - if ($entries) { - $this->cache->save($key, serialize($entries)); - } else { - $this->cache->delete($key); - } - return null; - } - - return $response; - } - - /** - * Hash a request URL into a string that returns cache metadata - * - * @param RequestInterface $request - * - * @return string - */ - protected function getCacheKey(RequestInterface $request) - { - // Allow cache.key_filter to trim down the URL cache key by removing generate query string values (e.g. auth) - if ($filter = $request->getParams()->get('cache.key_filter')) { - $url = $request->getUrl(true); - foreach (explode(',', $filter) as $remove) { - $url->getQuery()->remove(trim($remove)); - } - } else { - $url = $request->getUrl(); - } - - return $this->keyPrefix . md5($request->getMethod() . ' ' . $url); - } - - /** - * Create a cache key for a response's body - * - * @param string $url URL of the entry - * @param EntityBodyInterface $body Response body - * - * @return string - */ - protected function getBodyKey($url, EntityBodyInterface $body) - { - return $this->keyPrefix . md5($url) . $body->getContentMd5(); - } - - /** - * Determines whether two Request HTTP header sets are non-varying - * - * @param string $vary Response vary header - * @param array $r1 HTTP header array - * @param array $r2 HTTP header array - * - * @return bool - */ - private function requestsMatch($vary, $r1, $r2) - { - if ($vary) { - foreach (explode(',', $vary) as $header) { - $key = trim(strtolower($header)); - $v1 = isset($r1[$key]) ? $r1[$key] : null; - $v2 = isset($r2[$key]) ? $r2[$key] : null; - if ($v1 !== $v2) { - return false; - } - } - } - - return true; - } - - /** - * Creates an array of cacheable and normalized message headers - * - * @param MessageInterface $message - * - * @return array - */ - private function persistHeaders(MessageInterface $message) - { - // Headers are excluded from the caching (see RFC 2616:13.5.1) - static $noCache = array( - 'age' => true, - 'connection' => true, - 'keep-alive' => true, - 'proxy-authenticate' => true, - 'proxy-authorization' => true, - 'te' => true, - 'trailers' => true, - 'transfer-encoding' => true, - 'upgrade' => true, - 'set-cookie' => true, - 'set-cookie2' => true - ); - - // Clone the response to not destroy any necessary headers when caching - $headers = $message->getHeaders()->getAll(); - $headers = array_diff_key($headers, $noCache); - // Cast the headers to a string - $headers = array_map(function ($h) { return (string) $h; }, $headers); - - return $headers; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php deleted file mode 100644 index 3ca1fbf19..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultCanCacheStrategy.php +++ /dev/null @@ -1,32 +0,0 @@ -getMethod() != RequestInterface::GET && $request->getMethod() != RequestInterface::HEAD) { - return false; - } - - // Never cache requests when using no-store - if ($request->hasHeader('Cache-Control') && $request->getHeader('Cache-Control')->hasDirective('no-store')) { - return false; - } - - return true; - } - - public function canCacheResponse(Response $response) - { - return $response->isSuccessful() && $response->canCache(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php deleted file mode 100644 index af33234ee..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DefaultRevalidation.php +++ /dev/null @@ -1,174 +0,0 @@ -storage = $cache; - $this->canCache = $canCache ?: new DefaultCanCacheStrategy(); - } - - public function revalidate(RequestInterface $request, Response $response) - { - try { - $revalidate = $this->createRevalidationRequest($request, $response); - $validateResponse = $revalidate->send(); - if ($validateResponse->getStatusCode() == 200) { - return $this->handle200Response($request, $validateResponse); - } elseif ($validateResponse->getStatusCode() == 304) { - return $this->handle304Response($request, $validateResponse, $response); - } - } catch (BadResponseException $e) { - $this->handleBadResponse($e); - } - - // Other exceptions encountered in the revalidation request are ignored - // in hopes that sending a request to the origin server will fix it - return false; - } - - public function shouldRevalidate(RequestInterface $request, Response $response) - { - if ($request->getMethod() != RequestInterface::GET) { - return false; - } - - $reqCache = $request->getHeader('Cache-Control'); - $resCache = $response->getHeader('Cache-Control'); - - $revalidate = $request->getHeader('Pragma') == 'no-cache' || - ($reqCache && ($reqCache->hasDirective('no-cache') || $reqCache->hasDirective('must-revalidate'))) || - ($resCache && ($resCache->hasDirective('no-cache') || $resCache->hasDirective('must-revalidate'))); - - // Use the strong ETag validator if available and the response contains no Cache-Control directive - if (!$revalidate && !$resCache && $response->hasHeader('ETag')) { - $revalidate = true; - } - - return $revalidate; - } - - /** - * Handles a bad response when attempting to revalidate - * - * @param BadResponseException $e Exception encountered - * - * @throws BadResponseException - */ - protected function handleBadResponse(BadResponseException $e) - { - // 404 errors mean the resource no longer exists, so remove from - // cache, and prevent an additional request by throwing the exception - if ($e->getResponse()->getStatusCode() == 404) { - $this->storage->delete($e->getRequest()); - throw $e; - } - } - - /** - * Creates a request to use for revalidation - * - * @param RequestInterface $request Request - * @param Response $response Response to revalidate - * - * @return RequestInterface returns a revalidation request - */ - protected function createRevalidationRequest(RequestInterface $request, Response $response) - { - $revalidate = clone $request; - $revalidate->removeHeader('Pragma')->removeHeader('Cache-Control'); - - if ($response->getLastModified()) { - $revalidate->setHeader('If-Modified-Since', $response->getLastModified()); - } - - if ($response->getEtag()) { - $revalidate->setHeader('If-None-Match', $response->getEtag()); - } - - // Remove any cache plugins that might be on the request to prevent infinite recursive revalidations - $dispatcher = $revalidate->getEventDispatcher(); - foreach ($dispatcher->getListeners() as $eventName => $listeners) { - foreach ($listeners as $listener) { - if (is_array($listener) && $listener[0] instanceof CachePlugin) { - $dispatcher->removeListener($eventName, $listener); - } - } - } - - return $revalidate; - } - - /** - * Handles a 200 response response from revalidating. The server does not support validation, so use this response. - * - * @param RequestInterface $request Request that was sent - * @param Response $validateResponse Response received - * - * @return bool Returns true if valid, false if invalid - */ - protected function handle200Response(RequestInterface $request, Response $validateResponse) - { - $request->setResponse($validateResponse); - if ($this->canCache->canCacheResponse($validateResponse)) { - $this->storage->cache($request, $validateResponse); - } - - return false; - } - - /** - * Handle a 304 response and ensure that it is still valid - * - * @param RequestInterface $request Request that was sent - * @param Response $validateResponse Response received - * @param Response $response Original cached response - * - * @return bool Returns true if valid, false if invalid - */ - protected function handle304Response(RequestInterface $request, Response $validateResponse, Response $response) - { - static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); - - // Make sure that this response has the same ETag - if ($validateResponse->getEtag() != $response->getEtag()) { - return false; - } - - // Replace cached headers with any of these headers from the - // origin server that might be more up to date - $modified = false; - foreach ($replaceHeaders as $name) { - if ($validateResponse->hasHeader($name)) { - $modified = true; - $response->setHeader($name, $validateResponse->getHeader($name)); - } - } - - // Store the updated response in cache - if ($modified && $this->canCache->canCacheResponse($response)) { - $this->storage->cache($request, $response); - } - - return true; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php deleted file mode 100644 index 88b86f3ca..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cache/DenyRevalidation.php +++ /dev/null @@ -1,19 +0,0 @@ -=5.3.2", - "guzzle/http": "self.version", - "guzzle/cache": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Cache": "" } - }, - "target-dir": "Guzzle/Plugin/Cache", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php deleted file mode 100644 index 5218e5f0e..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Cookie.php +++ /dev/null @@ -1,538 +0,0 @@ - '', - 'value' => '', - 'domain' => '', - 'path' => '/', - 'expires' => null, - 'max_age' => 0, - 'comment' => null, - 'comment_url' => null, - 'port' => array(), - 'version' => null, - 'secure' => false, - 'discard' => false, - 'http_only' => false - ); - - $this->data = array_merge($defaults, $data); - // Extract the expires value and turn it into a UNIX timestamp if needed - if (!$this->getExpires() && $this->getMaxAge()) { - // Calculate the expires date - $this->setExpires(time() + (int) $this->getMaxAge()); - } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { - $this->setExpires(strtotime($this->getExpires())); - } - } - - /** - * Get the cookie as an array - * - * @return array - */ - public function toArray() - { - return $this->data; - } - - /** - * Get the cookie name - * - * @return string - */ - public function getName() - { - return $this->data['name']; - } - - /** - * Set the cookie name - * - * @param string $name Cookie name - * - * @return Cookie - */ - public function setName($name) - { - return $this->setData('name', $name); - } - - /** - * Get the cookie value - * - * @return string - */ - public function getValue() - { - return $this->data['value']; - } - - /** - * Set the cookie value - * - * @param string $value Cookie value - * - * @return Cookie - */ - public function setValue($value) - { - return $this->setData('value', $value); - } - - /** - * Get the domain - * - * @return string|null - */ - public function getDomain() - { - return $this->data['domain']; - } - - /** - * Set the domain of the cookie - * - * @param string $domain - * - * @return Cookie - */ - public function setDomain($domain) - { - return $this->setData('domain', $domain); - } - - /** - * Get the path - * - * @return string - */ - public function getPath() - { - return $this->data['path']; - } - - /** - * Set the path of the cookie - * - * @param string $path Path of the cookie - * - * @return Cookie - */ - public function setPath($path) - { - return $this->setData('path', $path); - } - - /** - * Maximum lifetime of the cookie in seconds - * - * @return int|null - */ - public function getMaxAge() - { - return $this->data['max_age']; - } - - /** - * Set the max-age of the cookie - * - * @param int $maxAge Max age of the cookie in seconds - * - * @return Cookie - */ - public function setMaxAge($maxAge) - { - return $this->setData('max_age', $maxAge); - } - - /** - * The UNIX timestamp when the cookie expires - * - * @return mixed - */ - public function getExpires() - { - return $this->data['expires']; - } - - /** - * Set the unix timestamp for which the cookie will expire - * - * @param int $timestamp Unix timestamp - * - * @return Cookie - */ - public function setExpires($timestamp) - { - return $this->setData('expires', $timestamp); - } - - /** - * Version of the cookie specification. RFC 2965 is 1 - * - * @return mixed - */ - public function getVersion() - { - return $this->data['version']; - } - - /** - * Set the cookie version - * - * @param string|int $version Version to set - * - * @return Cookie - */ - public function setVersion($version) - { - return $this->setData('version', $version); - } - - /** - * Get whether or not this is a secure cookie - * - * @return null|bool - */ - public function getSecure() - { - return $this->data['secure']; - } - - /** - * Set whether or not the cookie is secure - * - * @param bool $secure Set to true or false if secure - * - * @return Cookie - */ - public function setSecure($secure) - { - return $this->setData('secure', (bool) $secure); - } - - /** - * Get whether or not this is a session cookie - * - * @return null|bool - */ - public function getDiscard() - { - return $this->data['discard']; - } - - /** - * Set whether or not this is a session cookie - * - * @param bool $discard Set to true or false if this is a session cookie - * - * @return Cookie - */ - public function setDiscard($discard) - { - return $this->setData('discard', $discard); - } - - /** - * Get the comment - * - * @return string|null - */ - public function getComment() - { - return $this->data['comment']; - } - - /** - * Set the comment of the cookie - * - * @param string $comment Cookie comment - * - * @return Cookie - */ - public function setComment($comment) - { - return $this->setData('comment', $comment); - } - - /** - * Get the comment URL of the cookie - * - * @return string|null - */ - public function getCommentUrl() - { - return $this->data['comment_url']; - } - - /** - * Set the comment URL of the cookie - * - * @param string $commentUrl Cookie comment URL for more information - * - * @return Cookie - */ - public function setCommentUrl($commentUrl) - { - return $this->setData('comment_url', $commentUrl); - } - - /** - * Get an array of acceptable ports this cookie can be used with - * - * @return array - */ - public function getPorts() - { - return $this->data['port']; - } - - /** - * Set a list of acceptable ports this cookie can be used with - * - * @param array $ports Array of acceptable ports - * - * @return Cookie - */ - public function setPorts(array $ports) - { - return $this->setData('port', $ports); - } - - /** - * Get whether or not this is an HTTP only cookie - * - * @return bool - */ - public function getHttpOnly() - { - return $this->data['http_only']; - } - - /** - * Set whether or not this is an HTTP only cookie - * - * @param bool $httpOnly Set to true or false if this is HTTP only - * - * @return Cookie - */ - public function setHttpOnly($httpOnly) - { - return $this->setData('http_only', $httpOnly); - } - - /** - * Get an array of extra cookie data - * - * @return array - */ - public function getAttributes() - { - return $this->data['data']; - } - - /** - * Get a specific data point from the extra cookie data - * - * @param string $name Name of the data point to retrieve - * - * @return null|string - */ - public function getAttribute($name) - { - return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; - } - - /** - * Set a cookie data attribute - * - * @param string $name Name of the attribute to set - * @param string $value Value to set - * - * @return Cookie - */ - public function setAttribute($name, $value) - { - $this->data['data'][$name] = $value; - - return $this; - } - - /** - * Check if the cookie matches a path value - * - * @param string $path Path to check against - * - * @return bool - */ - public function matchesPath($path) - { - // RFC6265 http://tools.ietf.org/search/rfc6265#section-5.1.4 - // A request-path path-matches a given cookie-path if at least one of - // the following conditions holds: - - // o The cookie-path and the request-path are identical. - if ($path == $this->getPath()) { - return true; - } - - $pos = stripos($path, $this->getPath()); - if ($pos === 0) { - // o The cookie-path is a prefix of the request-path, and the last - // character of the cookie-path is %x2F ("/"). - if (substr($this->getPath(), -1, 1) === "/") { - return true; - } - - // o The cookie-path is a prefix of the request-path, and the first - // character of the request-path that is not included in the cookie- - // path is a %x2F ("/") character. - if (substr($path, strlen($this->getPath()), 1) === "/") { - return true; - } - } - - return false; - } - - /** - * Check if the cookie matches a domain value - * - * @param string $domain Domain to check against - * - * @return bool - */ - public function matchesDomain($domain) - { - // Remove the leading '.' as per spec in RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.2.3 - $cookieDomain = ltrim($this->getDomain(), '.'); - - // Domain not set or exact match. - if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) { - return true; - } - - // Matching the subdomain according to RFC 6265: http://tools.ietf.org/html/rfc6265#section-5.1.3 - if (filter_var($domain, FILTER_VALIDATE_IP)) { - return false; - } - - return (bool) preg_match('/\.' . preg_quote($cookieDomain, '/') . '$/i', $domain); - } - - /** - * Check if the cookie is compatible with a specific port - * - * @param int $port Port to check - * - * @return bool - */ - public function matchesPort($port) - { - return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); - } - - /** - * Check if the cookie is expired - * - * @return bool - */ - public function isExpired() - { - return $this->getExpires() && time() > $this->getExpires(); - } - - /** - * Check if the cookie is valid according to RFC 6265 - * - * @return bool|string Returns true if valid or an error message if invalid - */ - public function validate() - { - // Names must not be empty, but can be 0 - $name = $this->getName(); - if (empty($name) && !is_numeric($name)) { - return 'The cookie name must not be empty'; - } - - // Check if any of the invalid characters are present in the cookie name - if (strpbrk($name, self::getInvalidCharacters()) !== false) { - return 'The cookie name must not contain invalid characters: ' . $name; - } - - // Value must not be empty, but can be 0 - $value = $this->getValue(); - if (empty($value) && !is_numeric($value)) { - return 'The cookie value must not be empty'; - } - - // Domains must not be empty, but can be 0 - // A "0" is not a valid internet domain, but may be used as server name in a private network - $domain = $this->getDomain(); - if (empty($domain) && !is_numeric($domain)) { - return 'The cookie domain must not be empty'; - } - - return true; - } - - /** - * Set a value and return the cookie object - * - * @param string $key Key to set - * @param string $value Value to set - * - * @return Cookie - */ - private function setData($key, $value) - { - $this->data[$key] = $value; - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php deleted file mode 100644 index 6b675039f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/ArrayCookieJar.php +++ /dev/null @@ -1,237 +0,0 @@ -strictMode = $strictMode; - } - - /** - * Enable or disable strict mode on the cookie jar - * - * @param bool $strictMode Set to true to throw exceptions when invalid cookies are added. False to ignore them. - * - * @return self - */ - public function setStrictMode($strictMode) - { - $this->strictMode = $strictMode; - } - - public function remove($domain = null, $path = null, $name = null) - { - $cookies = $this->all($domain, $path, $name, false, false); - $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { - return !in_array($cookie, $cookies, true); - }); - - return $this; - } - - public function removeTemporary() - { - $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { - return !$cookie->getDiscard() && $cookie->getExpires(); - }); - - return $this; - } - - public function removeExpired() - { - $currentTime = time(); - $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { - return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); - }); - - return $this; - } - - public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) - { - return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( - $domain, - $path, - $name, - $skipDiscardable, - $skipExpired - ) { - return false === (($name && $cookie->getName() != $name) || - ($skipExpired && $cookie->isExpired()) || - ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || - ($path && !$cookie->matchesPath($path)) || - ($domain && !$cookie->matchesDomain($domain))); - })); - } - - public function add(Cookie $cookie) - { - // Only allow cookies with set and valid domain, name, value - $result = $cookie->validate(); - if ($result !== true) { - if ($this->strictMode) { - throw new InvalidCookieException($result); - } else { - $this->removeCookieIfEmpty($cookie); - return false; - } - } - - // Resolve conflicts with previously set cookies - foreach ($this->cookies as $i => $c) { - - // Two cookies are identical, when their path, domain, port and name are identical - if ($c->getPath() != $cookie->getPath() || - $c->getDomain() != $cookie->getDomain() || - $c->getPorts() != $cookie->getPorts() || - $c->getName() != $cookie->getName() - ) { - continue; - } - - // The previously set cookie is a discard cookie and this one is not so allow the new cookie to be set - if (!$cookie->getDiscard() && $c->getDiscard()) { - unset($this->cookies[$i]); - continue; - } - - // If the new cookie's expiration is further into the future, then replace the old cookie - if ($cookie->getExpires() > $c->getExpires()) { - unset($this->cookies[$i]); - continue; - } - - // If the value has changed, we better change it - if ($cookie->getValue() !== $c->getValue()) { - unset($this->cookies[$i]); - continue; - } - - // The cookie exists, so no need to continue - return false; - } - - $this->cookies[] = $cookie; - - return true; - } - - /** - * Serializes the cookie cookieJar - * - * @return string - */ - public function serialize() - { - // Only serialize long term cookies and unexpired cookies - return json_encode(array_map(function (Cookie $cookie) { - return $cookie->toArray(); - }, $this->all(null, null, null, true, true))); - } - - /** - * Unserializes the cookie cookieJar - */ - public function unserialize($data) - { - $data = json_decode($data, true); - if (empty($data)) { - $this->cookies = array(); - } else { - $this->cookies = array_map(function (array $cookie) { - return new Cookie($cookie); - }, $data); - } - } - - /** - * Returns the total number of stored cookies - * - * @return int - */ - public function count() - { - return count($this->cookies); - } - - /** - * Returns an iterator - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new \ArrayIterator($this->cookies); - } - - public function addCookiesFromResponse(Response $response, RequestInterface $request = null) - { - if ($cookieHeader = $response->getHeader('Set-Cookie')) { - $parser = ParserRegistry::getInstance()->getParser('cookie'); - foreach ($cookieHeader as $cookie) { - if ($parsed = $request - ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) - : $parser->parseCookie($cookie) - ) { - // Break up cookie v2 into multiple cookies - foreach ($parsed['cookies'] as $key => $value) { - $row = $parsed; - $row['name'] = $key; - $row['value'] = $value; - unset($row['cookies']); - $this->add(new Cookie($row)); - } - } - } - } - } - - public function getMatchingCookies(RequestInterface $request) - { - // Find cookies that match this request - $cookies = $this->all($request->getHost(), $request->getPath()); - // Remove ineligible cookies - foreach ($cookies as $index => $cookie) { - if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { - unset($cookies[$index]); - } - }; - - return $cookies; - } - - /** - * If a cookie already exists and the server asks to set it again with a null value, the - * cookie must be deleted. - * - * @param \Guzzle\Plugin\Cookie\Cookie $cookie - */ - private function removeCookieIfEmpty(Cookie $cookie) - { - $cookieValue = $cookie->getValue(); - if ($cookieValue === null || $cookieValue === '') { - $this->remove($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php deleted file mode 100644 index 7faa7d21f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookieJar/CookieJarInterface.php +++ /dev/null @@ -1,85 +0,0 @@ -filename = $cookieFile; - $this->load(); - } - - /** - * Saves the file when shutting down - */ - public function __destruct() - { - $this->persist(); - } - - /** - * Save the contents of the data array to the file - * - * @throws RuntimeException if the file cannot be found or created - */ - protected function persist() - { - if (false === file_put_contents($this->filename, $this->serialize())) { - // @codeCoverageIgnoreStart - throw new RuntimeException('Unable to open file ' . $this->filename); - // @codeCoverageIgnoreEnd - } - } - - /** - * Load the contents of the json formatted file into the data array and discard any unsaved state - */ - protected function load() - { - $json = file_get_contents($this->filename); - if (false === $json) { - // @codeCoverageIgnoreStart - throw new RuntimeException('Unable to open file ' . $this->filename); - // @codeCoverageIgnoreEnd - } - - $this->unserialize($json); - $this->cookies = $this->cookies ?: array(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php deleted file mode 100644 index df3210ee1..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/CookiePlugin.php +++ /dev/null @@ -1,70 +0,0 @@ -cookieJar = $cookieJar ?: new ArrayCookieJar(); - } - - public static function getSubscribedEvents() - { - return array( - 'request.before_send' => array('onRequestBeforeSend', 125), - 'request.sent' => array('onRequestSent', 125) - ); - } - - /** - * Get the cookie cookieJar - * - * @return CookieJarInterface - */ - public function getCookieJar() - { - return $this->cookieJar; - } - - /** - * Add cookies before a request is sent - * - * @param Event $event - */ - public function onRequestBeforeSend(Event $event) - { - $request = $event['request']; - if (!$request->getParams()->get('cookies.disable')) { - $request->removeHeader('Cookie'); - // Find cookies that match this request - foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { - $request->addCookie($cookie->getName(), $cookie->getValue()); - } - } - } - - /** - * Extract cookies from a sent request - * - * @param Event $event - */ - public function onRequestSent(Event $event) - { - $this->cookieJar->addCookiesFromResponse($event['response'], $event['request']); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php deleted file mode 100644 index b1fa6fd89..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Cookie/Exception/InvalidCookieException.php +++ /dev/null @@ -1,7 +0,0 @@ -=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Cookie": "" } - }, - "target-dir": "Guzzle/Plugin/Cookie", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php deleted file mode 100644 index 610e60cad..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/CurlAuthPlugin.php +++ /dev/null @@ -1,46 +0,0 @@ -getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest'); - */ -class CurlAuthPlugin implements EventSubscriberInterface -{ - private $username; - private $password; - private $scheme; - - /** - * @param string $username HTTP basic auth username - * @param string $password Password - * @param int $scheme Curl auth scheme - */ - public function __construct($username, $password, $scheme=CURLAUTH_BASIC) - { - Version::warn(__CLASS__ . " is deprecated. Use \$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');"); - $this->username = $username; - $this->password = $password; - $this->scheme = $scheme; - } - - public static function getSubscribedEvents() - { - return array('client.create_request' => array('onRequestCreate', 255)); - } - - /** - * Add basic auth - * - * @param Event $event - */ - public function onRequestCreate(Event $event) - { - $event['request']->setAuth($this->username, $this->password, $this->scheme); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json deleted file mode 100644 index edc8b24e5..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/CurlAuth/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-curlauth", - "description": "Guzzle cURL authorization plugin", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "curl", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\CurlAuth": "" } - }, - "target-dir": "Guzzle/Plugin/CurlAuth", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php deleted file mode 100644 index 5dce8bd6c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/ErrorResponseExceptionInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - array('onCommandBeforeSend', -1)); - } - - /** - * Adds a listener to requests before they sent from a command - * - * @param Event $event Event emitted - */ - public function onCommandBeforeSend(Event $event) - { - $command = $event['command']; - if ($operation = $command->getOperation()) { - if ($operation->getErrorResponses()) { - $request = $command->getRequest(); - $request->getEventDispatcher() - ->addListener('request.complete', $this->getErrorClosure($request, $command, $operation)); - } - } - } - - /** - * @param RequestInterface $request Request that received an error - * @param CommandInterface $command Command that created the request - * @param Operation $operation Operation that defines the request and errors - * - * @return \Closure Returns a closure - * @throws ErrorResponseException - */ - protected function getErrorClosure(RequestInterface $request, CommandInterface $command, Operation $operation) - { - return function (Event $event) use ($request, $command, $operation) { - $response = $event['response']; - foreach ($operation->getErrorResponses() as $error) { - if (!isset($error['class'])) { - continue; - } - if (isset($error['code']) && $response->getStatusCode() != $error['code']) { - continue; - } - if (isset($error['reason']) && $response->getReasonPhrase() != $error['reason']) { - continue; - } - $className = $error['class']; - $errorClassInterface = __NAMESPACE__ . '\\ErrorResponseExceptionInterface'; - if (!class_exists($className)) { - throw new ErrorResponseException("{$className} does not exist"); - } elseif (!(in_array($errorClassInterface, class_implements($className)))) { - throw new ErrorResponseException("{$className} must implement {$errorClassInterface}"); - } - throw $className::fromCommand($command, $response); - } - }; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php deleted file mode 100644 index 1d89e40e7..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/ErrorResponse/Exception/ErrorResponseException.php +++ /dev/null @@ -1,7 +0,0 @@ -=5.3.2", - "guzzle/service": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\ErrorResponse": "" } - }, - "target-dir": "Guzzle/Plugin/ErrorResponse", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php deleted file mode 100644 index 7375e892b..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/HistoryPlugin.php +++ /dev/null @@ -1,163 +0,0 @@ - array('onRequestSent', 9999)); - } - - /** - * Convert to a string that contains all request and response headers - * - * @return string - */ - public function __toString() - { - $lines = array(); - foreach ($this->transactions as $entry) { - $response = isset($entry['response']) ? $entry['response'] : ''; - $lines[] = '> ' . trim($entry['request']) . "\n\n< " . trim($response) . "\n"; - } - - return implode("\n", $lines); - } - - /** - * Add a request to the history - * - * @param RequestInterface $request Request to add - * @param Response $response Response of the request - * - * @return HistoryPlugin - */ - public function add(RequestInterface $request, Response $response = null) - { - if (!$response && $request->getResponse()) { - $response = $request->getResponse(); - } - - $this->transactions[] = array('request' => $request, 'response' => $response); - if (count($this->transactions) > $this->getlimit()) { - array_shift($this->transactions); - } - - return $this; - } - - /** - * Set the max number of requests to store - * - * @param int $limit Limit - * - * @return HistoryPlugin - */ - public function setLimit($limit) - { - $this->limit = (int) $limit; - - return $this; - } - - /** - * Get the request limit - * - * @return int - */ - public function getLimit() - { - return $this->limit; - } - - /** - * Get all of the raw transactions in the form of an array of associative arrays containing - * 'request' and 'response' keys. - * - * @return array - */ - public function getAll() - { - return $this->transactions; - } - - /** - * Get the requests in the history - * - * @return \ArrayIterator - */ - public function getIterator() - { - // Return an iterator just like the old iteration of the HistoryPlugin for BC compatibility (use getAll()) - return new \ArrayIterator(array_map(function ($entry) { - $entry['request']->getParams()->set('actual_response', $entry['response']); - return $entry['request']; - }, $this->transactions)); - } - - /** - * Get the number of requests in the history - * - * @return int - */ - public function count() - { - return count($this->transactions); - } - - /** - * Get the last request sent - * - * @return RequestInterface - */ - public function getLastRequest() - { - $last = end($this->transactions); - - return $last['request']; - } - - /** - * Get the last response in the history - * - * @return Response|null - */ - public function getLastResponse() - { - $last = end($this->transactions); - - return isset($last['response']) ? $last['response'] : null; - } - - /** - * Clears the history - * - * @return HistoryPlugin - */ - public function clear() - { - $this->transactions = array(); - - return $this; - } - - public function onRequestSent(Event $event) - { - $this->add($event['request'], $event['response']); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json deleted file mode 100644 index ba0bf2c4d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/History/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-history", - "description": "Guzzle history plugin", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\History": "" } - }, - "target-dir": "Guzzle/Plugin/History", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php deleted file mode 100644 index cabdea854..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/LogPlugin.php +++ /dev/null @@ -1,161 +0,0 @@ -logAdapter = $logAdapter; - $this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter); - $this->wireBodies = $wireBodies; - } - - /** - * Get a log plugin that outputs full request, response, and curl error information to stderr - * - * @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable - * @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available - * - * @return self - */ - public static function getDebugPlugin($wireBodies = true, $stream = null) - { - if ($stream === null) { - if (defined('STDERR')) { - $stream = STDERR; - } else { - $stream = fopen('php://output', 'w'); - } - } - - return new self(new ClosureLogAdapter(function ($m) use ($stream) { - fwrite($stream, $m . PHP_EOL); - }), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies); - } - - public static function getSubscribedEvents() - { - return array( - 'curl.callback.write' => array('onCurlWrite', 255), - 'curl.callback.read' => array('onCurlRead', 255), - 'request.before_send' => array('onRequestBeforeSend', 255), - 'request.sent' => array('onRequestSent', 255) - ); - } - - /** - * Event triggered when curl data is read from a request - * - * @param Event $event - */ - public function onCurlRead(Event $event) - { - // Stream the request body to the log if the body is not repeatable - if ($wire = $event['request']->getParams()->get('request_wire')) { - $wire->write($event['read']); - } - } - - /** - * Event triggered when curl data is written to a response - * - * @param Event $event - */ - public function onCurlWrite(Event $event) - { - // Stream the response body to the log if the body is not repeatable - if ($wire = $event['request']->getParams()->get('response_wire')) { - $wire->write($event['write']); - } - } - - /** - * Called before a request is sent - * - * @param Event $event - */ - public function onRequestBeforeSend(Event $event) - { - if ($this->wireBodies) { - $request = $event['request']; - // Ensure that curl IO events are emitted - $request->getCurlOptions()->set('emit_io', true); - // We need to make special handling for content wiring and non-repeatable streams. - if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() - && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable()) - ) { - // The body of the request cannot be recalled so logging the body will require us to buffer it - $request->getParams()->set('request_wire', EntityBody::factory()); - } - if (!$request->getResponseBody()->isRepeatable()) { - // The body of the response cannot be recalled so logging the body will require us to buffer it - $request->getParams()->set('response_wire', EntityBody::factory()); - } - } - } - - /** - * Triggers the actual log write when a request completes - * - * @param Event $event - */ - public function onRequestSent(Event $event) - { - $request = $event['request']; - $response = $event['response']; - $handle = $event['handle']; - - if ($wire = $request->getParams()->get('request_wire')) { - $request = clone $request; - $request->setBody($wire); - } - - if ($wire = $request->getParams()->get('response_wire')) { - $response = clone $response; - $response->setBody($wire); - } - - // Send the log message to the adapter, adding a category and host - $priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG; - $message = $this->formatter->format($request, $response, $handle); - $this->logAdapter->log($message, $priority, array( - 'request' => $request, - 'response' => $response, - 'handle' => $handle - )); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json deleted file mode 100644 index 130e6da0a..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Log/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "guzzle/plugin-log", - "description": "Guzzle log plugin for over the wire logging", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "log", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version", - "guzzle/log": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Log": "" } - }, - "target-dir": "Guzzle/Plugin/Log", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php deleted file mode 100644 index 851242433..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/CommandContentMd5Plugin.php +++ /dev/null @@ -1,57 +0,0 @@ -contentMd5Param = $contentMd5Param; - $this->validateMd5Param = $validateMd5Param; - } - - public static function getSubscribedEvents() - { - return array('command.before_send' => array('onCommandBeforeSend', -255)); - } - - public function onCommandBeforeSend(Event $event) - { - $command = $event['command']; - $request = $command->getRequest(); - - // Only add an MD5 is there is a MD5 option on the operation and it has a payload - if ($request instanceof EntityEnclosingRequestInterface && $request->getBody() - && $command->getOperation()->hasParam($this->contentMd5Param)) { - // Check if an MD5 checksum value should be passed along to the request - if ($command[$this->contentMd5Param] === true) { - if (false !== ($md5 = $request->getBody()->getContentMd5(true, true))) { - $request->setHeader('Content-MD5', $md5); - } - } - } - - // Check if MD5 validation should be used with the response - if ($command[$this->validateMd5Param] === true) { - $request->addSubscriber(new Md5ValidatorPlugin(true, false)); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php deleted file mode 100644 index 5d7a3785e..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/Md5ValidatorPlugin.php +++ /dev/null @@ -1,88 +0,0 @@ -contentLengthCutoff = $contentLengthCutoff; - $this->contentEncoded = $contentEncoded; - } - - public static function getSubscribedEvents() - { - return array('request.complete' => array('onRequestComplete', 255)); - } - - /** - * {@inheritdoc} - * @throws UnexpectedValueException - */ - public function onRequestComplete(Event $event) - { - $response = $event['response']; - - if (!$contentMd5 = $response->getContentMd5()) { - return; - } - - $contentEncoding = $response->getContentEncoding(); - if ($contentEncoding && !$this->contentEncoded) { - return false; - } - - // Make sure that the size of the request is under the cutoff size - if ($this->contentLengthCutoff) { - $size = $response->getContentLength() ?: $response->getBody()->getSize(); - if (!$size || $size > $this->contentLengthCutoff) { - return; - } - } - - if (!$contentEncoding) { - $hash = $response->getBody()->getContentMd5(); - } elseif ($contentEncoding == 'gzip') { - $response->getBody()->compress('zlib.deflate'); - $hash = $response->getBody()->getContentMd5(); - $response->getBody()->uncompress(); - } elseif ($contentEncoding == 'compress') { - $response->getBody()->compress('bzip2.compress'); - $hash = $response->getBody()->getContentMd5(); - $response->getBody()->uncompress(); - } else { - return; - } - - if ($contentMd5 !== $hash) { - throw new UnexpectedValueException( - "The response entity body may have been modified over the wire. The Content-MD5 " - . "received ({$contentMd5}) did not match the calculated MD5 hash ({$hash})." - ); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json deleted file mode 100644 index 0602d0609..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Md5/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-md5", - "description": "Guzzle MD5 plugins", - "homepage": "http://guzzlephp.org/", - "keywords": ["plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Md5": "" } - }, - "target-dir": "Guzzle/Plugin/Md5", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php deleted file mode 100644 index 2440578cf..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/MockPlugin.php +++ /dev/null @@ -1,245 +0,0 @@ -readBodies = $readBodies; - $this->temporary = $temporary; - if ($items) { - foreach ($items as $item) { - if ($item instanceof \Exception) { - $this->addException($item); - } else { - $this->addResponse($item); - } - } - } - } - - public static function getSubscribedEvents() - { - // Use a number lower than the CachePlugin - return array('request.before_send' => array('onRequestBeforeSend', -999)); - } - - public static function getAllEvents() - { - return array('mock.request'); - } - - /** - * Get a mock response from a file - * - * @param string $path File to retrieve a mock response from - * - * @return Response - * @throws InvalidArgumentException if the file is not found - */ - public static function getMockFile($path) - { - if (!file_exists($path)) { - throw new InvalidArgumentException('Unable to open mock file: ' . $path); - } - - return Response::fromMessage(file_get_contents($path)); - } - - /** - * Set whether or not to consume the entity body of a request when a mock - * response is used - * - * @param bool $readBodies Set to true to read and consume entity bodies - * - * @return self - */ - public function readBodies($readBodies) - { - $this->readBodies = $readBodies; - - return $this; - } - - /** - * Returns the number of remaining mock responses - * - * @return int - */ - public function count() - { - return count($this->queue); - } - - /** - * Add a response to the end of the queue - * - * @param string|Response $response Response object or path to response file - * - * @return MockPlugin - * @throws InvalidArgumentException if a string or Response is not passed - */ - public function addResponse($response) - { - if (!($response instanceof Response)) { - if (!is_string($response)) { - throw new InvalidArgumentException('Invalid response'); - } - $response = self::getMockFile($response); - } - - $this->queue[] = $response; - - return $this; - } - - /** - * Add an exception to the end of the queue - * - * @param CurlException $e Exception to throw when the request is executed - * - * @return MockPlugin - */ - public function addException(CurlException $e) - { - $this->queue[] = $e; - - return $this; - } - - /** - * Clear the queue - * - * @return MockPlugin - */ - public function clearQueue() - { - $this->queue = array(); - - return $this; - } - - /** - * Returns an array of mock responses remaining in the queue - * - * @return array - */ - public function getQueue() - { - return $this->queue; - } - - /** - * Check if this is a temporary plugin - * - * @return bool - */ - public function isTemporary() - { - return $this->temporary; - } - - /** - * Get a response from the front of the list and add it to a request - * - * @param RequestInterface $request Request to mock - * - * @return self - * @throws CurlException When request.send is called and an exception is queued - */ - public function dequeue(RequestInterface $request) - { - $this->dispatch('mock.request', array('plugin' => $this, 'request' => $request)); - - $item = array_shift($this->queue); - if ($item instanceof Response) { - if ($this->readBodies && $request instanceof EntityEnclosingRequestInterface) { - $request->getEventDispatcher()->addListener('request.sent', $f = function (Event $event) use (&$f) { - while ($data = $event['request']->getBody()->read(8096)); - // Remove the listener after one-time use - $event['request']->getEventDispatcher()->removeListener('request.sent', $f); - }); - } - $request->setResponse($item); - } elseif ($item instanceof CurlException) { - // Emulates exceptions encountered while transferring requests - $item->setRequest($request); - $state = $request->setState(RequestInterface::STATE_ERROR, array('exception' => $item)); - // Only throw if the exception wasn't handled - if ($state == RequestInterface::STATE_ERROR) { - throw $item; - } - } - - return $this; - } - - /** - * Clear the array of received requests - */ - public function flush() - { - $this->received = array(); - } - - /** - * Get an array of requests that were mocked by this plugin - * - * @return array - */ - public function getReceivedRequests() - { - return $this->received; - } - - /** - * Called when a request is about to be sent - * - * @param Event $event - * @throws \OutOfBoundsException When queue is empty - */ - public function onRequestBeforeSend(Event $event) - { - if (!$this->queue) { - throw new \OutOfBoundsException('Mock queue is empty'); - } - - $request = $event['request']; - $this->received[] = $request; - // Detach the filter from the client so it's a one-time use - if ($this->temporary && count($this->queue) == 1 && $request->getClient()) { - $request->getClient()->getEventDispatcher()->removeSubscriber($this); - } - $this->dequeue($request); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json deleted file mode 100644 index f8201e31f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Mock/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-mock", - "description": "Guzzle Mock plugin", - "homepage": "http://guzzlephp.org/", - "keywords": ["mock", "plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Mock": "" } - }, - "target-dir": "Guzzle/Plugin/Mock", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php deleted file mode 100644 index 95e0c3e4a..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/OauthPlugin.php +++ /dev/null @@ -1,306 +0,0 @@ -config = Collection::fromConfig($config, array( - 'version' => '1.0', - 'request_method' => self::REQUEST_METHOD_HEADER, - 'consumer_key' => 'anonymous', - 'consumer_secret' => 'anonymous', - 'signature_method' => 'HMAC-SHA1', - 'signature_callback' => function($stringToSign, $key) { - return hash_hmac('sha1', $stringToSign, $key, true); - } - ), array( - 'signature_method', 'signature_callback', 'version', - 'consumer_key', 'consumer_secret' - )); - } - - public static function getSubscribedEvents() - { - return array( - 'request.before_send' => array('onRequestBeforeSend', -1000) - ); - } - - /** - * Request before-send event handler - * - * @param Event $event Event received - * @return array - * @throws \InvalidArgumentException - */ - public function onRequestBeforeSend(Event $event) - { - $timestamp = $this->getTimestamp($event); - $request = $event['request']; - $nonce = $this->generateNonce($request); - $authorizationParams = $this->getOauthParams($timestamp, $nonce); - $authorizationParams['oauth_signature'] = $this->getSignature($request, $timestamp, $nonce); - - switch ($this->config['request_method']) { - case self::REQUEST_METHOD_HEADER: - $request->setHeader( - 'Authorization', - $this->buildAuthorizationHeader($authorizationParams) - ); - break; - case self::REQUEST_METHOD_QUERY: - foreach ($authorizationParams as $key => $value) { - $request->getQuery()->set($key, $value); - } - break; - default: - throw new \InvalidArgumentException(sprintf( - 'Invalid consumer method "%s"', - $this->config['request_method'] - )); - } - - return $authorizationParams; - } - - /** - * Builds the Authorization header for a request - * - * @param array $authorizationParams Associative array of authorization parameters - * - * @return string - */ - private function buildAuthorizationHeader($authorizationParams) - { - $authorizationString = 'OAuth '; - foreach ($authorizationParams as $key => $val) { - if ($val) { - $authorizationString .= $key . '="' . urlencode($val) . '", '; - } - } - - return substr($authorizationString, 0, -2); - } - - /** - * Calculate signature for request - * - * @param RequestInterface $request Request to generate a signature for - * @param integer $timestamp Timestamp to use for nonce - * @param string $nonce - * - * @return string - */ - public function getSignature(RequestInterface $request, $timestamp, $nonce) - { - $string = $this->getStringToSign($request, $timestamp, $nonce); - $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); - - return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); - } - - /** - * Calculate string to sign - * - * @param RequestInterface $request Request to generate a signature for - * @param int $timestamp Timestamp to use for nonce - * @param string $nonce - * - * @return string - */ - public function getStringToSign(RequestInterface $request, $timestamp, $nonce) - { - $params = $this->getParamsToSign($request, $timestamp, $nonce); - - // Convert booleans to strings. - $params = $this->prepareParameters($params); - - // Build signing string from combined params - $parameterString = clone $request->getQuery(); - $parameterString->replace($params); - - $url = Url::factory($request->getUrl())->setQuery('')->setFragment(null); - - return strtoupper($request->getMethod()) . '&' - . rawurlencode($url) . '&' - . rawurlencode((string) $parameterString); - } - - /** - * Get the oauth parameters as named by the oauth spec - * - * @param $timestamp - * @param $nonce - * @return Collection - */ - protected function getOauthParams($timestamp, $nonce) - { - $params = new Collection(array( - 'oauth_consumer_key' => $this->config['consumer_key'], - 'oauth_nonce' => $nonce, - 'oauth_signature_method' => $this->config['signature_method'], - 'oauth_timestamp' => $timestamp, - )); - - // Optional parameters should not be set if they have not been set in the config as - // the parameter may be considered invalid by the Oauth service. - $optionalParams = array( - 'callback' => 'oauth_callback', - 'token' => 'oauth_token', - 'verifier' => 'oauth_verifier', - 'version' => 'oauth_version' - ); - - foreach ($optionalParams as $optionName => $oauthName) { - if (isset($this->config[$optionName]) == true) { - $params[$oauthName] = $this->config[$optionName]; - } - } - - return $params; - } - - /** - * Get all of the parameters required to sign a request including: - * * The oauth params - * * The request GET params - * * The params passed in the POST body (with a content-type of application/x-www-form-urlencoded) - * - * @param RequestInterface $request Request to generate a signature for - * @param integer $timestamp Timestamp to use for nonce - * @param string $nonce - * - * @return array - */ - public function getParamsToSign(RequestInterface $request, $timestamp, $nonce) - { - $params = $this->getOauthParams($timestamp, $nonce); - - // Add query string parameters - $params->merge($request->getQuery()); - - // Add POST fields to signing string if required - if ($this->shouldPostFieldsBeSigned($request)) - { - $params->merge($request->getPostFields()); - } - - // Sort params - $params = $params->toArray(); - uksort($params, 'strcmp'); - - return $params; - } - - /** - * Decide whether the post fields should be added to the base string that Oauth signs. - * This implementation is correct. Non-conformant APIs may require that this method be - * overwritten e.g. the Flickr API incorrectly adds the post fields when the Content-Type - * is 'application/x-www-form-urlencoded' - * - * @param $request - * @return bool Whether the post fields should be signed or not - */ - public function shouldPostFieldsBeSigned($request) - { - if (!$this->config->get('disable_post_params') && - $request instanceof EntityEnclosingRequestInterface && - false !== strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) - { - return true; - } - - return false; - } - - /** - * Returns a Nonce Based on the unique id and URL. This will allow for multiple requests in parallel with the same - * exact timestamp to use separate nonce's. - * - * @param RequestInterface $request Request to generate a nonce for - * - * @return string - */ - public function generateNonce(RequestInterface $request) - { - return sha1(uniqid('', true) . $request->getUrl()); - } - - /** - * Gets timestamp from event or create new timestamp - * - * @param Event $event Event containing contextual information - * - * @return int - */ - public function getTimestamp(Event $event) - { - return $event['timestamp'] ?: time(); - } - - /** - * Convert booleans to strings, removed unset parameters, and sorts the array - * - * @param array $data Data array - * - * @return array - */ - protected function prepareParameters($data) - { - ksort($data); - foreach ($data as $key => &$value) { - switch (gettype($value)) { - case 'NULL': - unset($data[$key]); - break; - case 'array': - $data[$key] = self::prepareParameters($value); - break; - case 'boolean': - $data[$key] = $value ? 'true' : 'false'; - break; - } - } - - return $data; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json deleted file mode 100644 index c9766ba16..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/Oauth/composer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "guzzle/plugin-oauth", - "description": "Guzzle OAuth plugin", - "homepage": "http://guzzlephp.org/", - "keywords": ["oauth", "plugin", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin\\Oauth": "" } - }, - "target-dir": "Guzzle/Plugin/Oauth", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json b/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json deleted file mode 100644 index 2bbe64cc5..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Plugin/composer.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "guzzle/plugin", - "description": "Guzzle plugin component containing all Guzzle HTTP plugins", - "homepage": "http://guzzlephp.org/", - "keywords": ["http", "client", "plugin", "extension", "guzzle"], - "license": "MIT", - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "require": { - "php": ">=5.3.2", - "guzzle/http": "self.version" - }, - "suggest": { - "guzzle/cache": "self.version", - "guzzle/log": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Plugin": "" } - }, - "target-dir": "Guzzle/Plugin", - "replace": { - "guzzle/plugin-async": "self.version", - "guzzle/plugin-backoff": "self.version", - "guzzle/plugin-cache": "self.version", - "guzzle/plugin-cookie": "self.version", - "guzzle/plugin-curlauth": "self.version", - "guzzle/plugin-error-response": "self.version", - "guzzle/plugin-history": "self.version", - "guzzle/plugin-log": "self.version", - "guzzle/plugin-md5": "self.version", - "guzzle/plugin-mock": "self.version", - "guzzle/plugin-oauth": "self.version" - }, - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php deleted file mode 100644 index cd06f5722..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/AbstractConfigLoader.php +++ /dev/null @@ -1,177 +0,0 @@ - 'JSON_ERROR_NONE - No errors', - JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded', - JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch', - JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found', - JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON', - JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded' - ); - - public function load($config, array $options = array()) - { - // Reset the array of loaded files because this is a new config - $this->loadedFiles = array(); - - if (is_string($config)) { - $config = $this->loadFile($config); - } elseif (!is_array($config)) { - throw new InvalidArgumentException('Unknown type passed to configuration loader: ' . gettype($config)); - } else { - $this->mergeIncludes($config); - } - - return $this->build($config, $options); - } - - /** - * Add an include alias to the loader - * - * @param string $filename Filename to alias (e.g. _foo) - * @param string $alias Actual file to use (e.g. /path/to/foo.json) - * - * @return self - */ - public function addAlias($filename, $alias) - { - $this->aliases[$filename] = $alias; - - return $this; - } - - /** - * Remove an alias from the loader - * - * @param string $alias Alias to remove - * - * @return self - */ - public function removeAlias($alias) - { - unset($this->aliases[$alias]); - - return $this; - } - - /** - * Perform the parsing of a config file and create the end result - * - * @param array $config Configuration data - * @param array $options Options to use when building - * - * @return mixed - */ - protected abstract function build($config, array $options); - - /** - * Load a configuration file (can load JSON or PHP files that return an array when included) - * - * @param string $filename File to load - * - * @return array - * @throws InvalidArgumentException - * @throws RuntimeException when the JSON cannot be parsed - */ - protected function loadFile($filename) - { - if (isset($this->aliases[$filename])) { - $filename = $this->aliases[$filename]; - } - - switch (pathinfo($filename, PATHINFO_EXTENSION)) { - case 'js': - case 'json': - $level = error_reporting(0); - $json = file_get_contents($filename); - error_reporting($level); - - if ($json === false) { - $err = error_get_last(); - throw new InvalidArgumentException("Unable to open {$filename}: " . $err['message']); - } - - $config = json_decode($json, true); - // Throw an exception if there was an error loading the file - if ($error = json_last_error()) { - $message = isset(self::$jsonErrors[$error]) ? self::$jsonErrors[$error] : 'Unknown error'; - throw new RuntimeException("Error loading JSON data from {$filename}: ({$error}) - {$message}"); - } - break; - case 'php': - if (!is_readable($filename)) { - throw new InvalidArgumentException("Unable to open {$filename} for reading"); - } - $config = require $filename; - if (!is_array($config)) { - throw new InvalidArgumentException('PHP files must return an array of configuration data'); - } - break; - default: - throw new InvalidArgumentException('Unknown file extension: ' . $filename); - } - - // Keep track of this file being loaded to prevent infinite recursion - $this->loadedFiles[$filename] = true; - - // Merge include files into the configuration array - $this->mergeIncludes($config, dirname($filename)); - - return $config; - } - - /** - * Merges in all include files - * - * @param array $config Config data that contains includes - * @param string $basePath Base path to use when a relative path is encountered - * - * @return array Returns the merged and included data - */ - protected function mergeIncludes(&$config, $basePath = null) - { - if (!empty($config['includes'])) { - foreach ($config['includes'] as &$path) { - // Account for relative paths - if ($path[0] != DIRECTORY_SEPARATOR && !isset($this->aliases[$path]) && $basePath) { - $path = "{$basePath}/{$path}"; - } - // Don't load the same files more than once - if (!isset($this->loadedFiles[$path])) { - $this->loadedFiles[$path] = true; - $config = $this->mergeData($this->loadFile($path), $config); - } - } - } - } - - /** - * Default implementation for merging two arrays of data (uses array_merge_recursive) - * - * @param array $a Original data - * @param array $b Data to merge into the original and overwrite existing values - * - * @return array - */ - protected function mergeData(array $a, array $b) - { - return array_merge_recursive($a, $b); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php deleted file mode 100644 index 38150db4b..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php +++ /dev/null @@ -1,189 +0,0 @@ -load($config, $globalParameters); - } - - /** - * @param array $serviceBuilderConfig Service configuration settings: - * - name: Name of the service - * - class: Client class to instantiate using a factory method - * - params: array of key value pair configuration settings for the builder - */ - public function __construct(array $serviceBuilderConfig = array()) - { - $this->builderConfig = $serviceBuilderConfig; - } - - public static function getAllEvents() - { - return array('service_builder.create_client'); - } - - public function unserialize($serialized) - { - $this->builderConfig = json_decode($serialized, true); - } - - public function serialize() - { - return json_encode($this->builderConfig); - } - - /** - * Attach a plugin to every client created by the builder - * - * @param EventSubscriberInterface $plugin Plugin to attach to each client - * - * @return self - */ - public function addGlobalPlugin(EventSubscriberInterface $plugin) - { - $this->plugins[] = $plugin; - - return $this; - } - - /** - * Get data from the service builder without triggering the building of a service - * - * @param string $name Name of the service to retrieve - * - * @return array|null - */ - public function getData($name) - { - return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null; - } - - public function get($name, $throwAway = false) - { - if (!isset($this->builderConfig[$name])) { - - // Check to see if arbitrary data is being referenced - if (isset($this->clients[$name])) { - return $this->clients[$name]; - } - - // Check aliases and return a match if found - foreach ($this->builderConfig as $actualName => $config) { - if (isset($config['alias']) && $config['alias'] == $name) { - return $this->get($actualName, $throwAway); - } - } - throw new ServiceNotFoundException('No service is registered as ' . $name); - } - - if (!$throwAway && isset($this->clients[$name])) { - return $this->clients[$name]; - } - - $builder =& $this->builderConfig[$name]; - - // Convert references to the actual client - foreach ($builder['params'] as &$v) { - if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') { - $v = $this->get(trim($v, '{} ')); - } - } - - // Get the configured parameters and merge in any parameters provided for throw-away clients - $config = $builder['params']; - if (is_array($throwAway)) { - $config = $throwAway + $config; - } - - $client = $builder['class']::factory($config); - - if (!$throwAway) { - $this->clients[$name] = $client; - } - - if ($client instanceof ClientInterface) { - foreach ($this->plugins as $plugin) { - $client->addSubscriber($plugin); - } - // Dispatch an event letting listeners know a client was created - $this->dispatch('service_builder.create_client', array('client' => $client)); - } - - return $client; - } - - public function set($key, $service) - { - if (is_array($service) && isset($service['class']) && isset($service['params'])) { - $this->builderConfig[$key] = $service; - } else { - $this->clients[$key] = $service; - } - - return $this; - } - - public function offsetSet($offset, $value) - { - $this->set($offset, $value); - } - - public function offsetUnset($offset) - { - unset($this->builderConfig[$offset]); - unset($this->clients[$offset]); - } - - public function offsetExists($offset) - { - return isset($this->builderConfig[$offset]) || isset($this->clients[$offset]); - } - - public function offsetGet($offset) - { - return $this->get($offset); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php deleted file mode 100644 index 4fc310a47..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderInterface.php +++ /dev/null @@ -1,40 +0,0 @@ - &$service) { - - $service['params'] = isset($service['params']) ? $service['params'] : array(); - - // Check if this client builder extends another client - if (!empty($service['extends'])) { - - // Make sure that the service it's extending has been defined - if (!isset($services[$service['extends']])) { - throw new ServiceNotFoundException( - "{$name} is trying to extend a non-existent service: {$service['extends']}" - ); - } - - $extended = &$services[$service['extends']]; - - // Use the correct class attribute - if (empty($service['class'])) { - $service['class'] = isset($extended['class']) ? $extended['class'] : ''; - } - if ($extendsParams = isset($extended['params']) ? $extended['params'] : false) { - $service['params'] = $service['params'] + $extendsParams; - } - } - - // Overwrite default values with global parameter values - if (!empty($options)) { - $service['params'] = $options + $service['params']; - } - - $service['class'] = isset($service['class']) ? $service['class'] : ''; - } - - return new $class($services); - } - - protected function mergeData(array $a, array $b) - { - $result = $b + $a; - - // Merge services using a recursive union of arrays - if (isset($a['services']) && $b['services']) { - - // Get a union of the services of the two arrays - $result['services'] = $b['services'] + $a['services']; - - // Merge each service in using a union of the two arrays - foreach ($result['services'] as $name => &$service) { - - // By default, services completely override a previously defined service unless it extends itself - if (isset($a['services'][$name]['extends']) - && isset($b['services'][$name]['extends']) - && $b['services'][$name]['extends'] == $name - ) { - $service += $a['services'][$name]; - // Use the `extends` attribute of the parent - $service['extends'] = $a['services'][$name]['extends']; - // Merge parameters using a union if both have parameters - if (isset($a['services'][$name]['params'])) { - $service['params'] += $a['services'][$name]['params']; - } - } - } - } - - return $result; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php deleted file mode 100644 index 26f8360cc..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/CachingConfigLoader.php +++ /dev/null @@ -1,46 +0,0 @@ -loader = $loader; - $this->cache = $cache; - } - - public function load($config, array $options = array()) - { - if (!is_string($config)) { - $key = false; - } else { - $key = 'loader_' . crc32($config); - if ($result = $this->cache->fetch($key)) { - return $result; - } - } - - $result = $this->loader->load($config, $options); - if ($key) { - $this->cache->save($key, $result); - } - - return $result; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php deleted file mode 100644 index 3e5f8e53d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Client.php +++ /dev/null @@ -1,297 +0,0 @@ -getCommand($method, isset($args[0]) ? $args[0] : array())->getResult(); - } - - public function getCommand($name, array $args = array()) - { - // Add global client options to the command - if ($options = $this->getConfig(self::COMMAND_PARAMS)) { - $args += $options; - } - - if (!($command = $this->getCommandFactory()->factory($name, $args))) { - throw new InvalidArgumentException("Command was not found matching {$name}"); - } - - $command->setClient($this); - $this->dispatch('client.command.create', array('client' => $this, 'command' => $command)); - - return $command; - } - - /** - * Set the command factory used to create commands by name - * - * @param CommandFactoryInterface $factory Command factory - * - * @return self - */ - public function setCommandFactory(CommandFactoryInterface $factory) - { - $this->commandFactory = $factory; - - return $this; - } - - /** - * Set the resource iterator factory associated with the client - * - * @param ResourceIteratorFactoryInterface $factory Resource iterator factory - * - * @return self - */ - public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) - { - $this->resourceIteratorFactory = $factory; - - return $this; - } - - public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) - { - if (!($command instanceof CommandInterface)) { - $command = $this->getCommand($command, $commandOptions ?: array()); - } - - return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); - } - - public function execute($command) - { - if ($command instanceof CommandInterface) { - $this->send($this->prepareCommand($command)); - $this->dispatch('command.after_send', array('command' => $command)); - return $command->getResult(); - } elseif (is_array($command) || $command instanceof \Traversable) { - return $this->executeMultiple($command); - } else { - throw new InvalidArgumentException('Command must be a command or array of commands'); - } - } - - public function setDescription(ServiceDescriptionInterface $service) - { - $this->serviceDescription = $service; - - if ($this->getCommandFactory() && $this->getCommandFactory() instanceof CompositeFactory) { - $this->commandFactory->add(new Command\Factory\ServiceDescriptionFactory($service)); - } - - // If a baseUrl was set on the description, then update the client - if ($baseUrl = $service->getBaseUrl()) { - $this->setBaseUrl($baseUrl); - } - - return $this; - } - - public function getDescription() - { - return $this->serviceDescription; - } - - /** - * Set the inflector used with the client - * - * @param InflectorInterface $inflector Inflection object - * - * @return self - */ - public function setInflector(InflectorInterface $inflector) - { - $this->inflector = $inflector; - - return $this; - } - - /** - * Get the inflector used with the client - * - * @return self - */ - public function getInflector() - { - if (!$this->inflector) { - $this->inflector = Inflector::getDefault(); - } - - return $this->inflector; - } - - /** - * Prepare a command for sending and get the RequestInterface object created by the command - * - * @param CommandInterface $command Command to prepare - * - * @return RequestInterface - */ - protected function prepareCommand(CommandInterface $command) - { - // Set the client and prepare the command - $request = $command->setClient($this)->prepare(); - // Set the state to new if the command was previously executed - $request->setState(RequestInterface::STATE_NEW); - $this->dispatch('command.before_send', array('command' => $command)); - - return $request; - } - - /** - * Execute multiple commands in parallel - * - * @param array|Traversable $commands Array of CommandInterface objects to execute - * - * @return array Returns an array of the executed commands - * @throws Exception\CommandTransferException - */ - protected function executeMultiple($commands) - { - $requests = array(); - $commandRequests = new \SplObjectStorage(); - - foreach ($commands as $command) { - $request = $this->prepareCommand($command); - $commandRequests[$request] = $command; - $requests[] = $request; - } - - try { - $this->send($requests); - foreach ($commands as $command) { - $this->dispatch('command.after_send', array('command' => $command)); - } - return $commands; - } catch (MultiTransferException $failureException) { - // Throw a CommandTransferException using the successful and failed commands - $e = CommandTransferException::fromMultiTransferException($failureException); - - // Remove failed requests from the successful requests array and add to the failures array - foreach ($failureException->getFailedRequests() as $request) { - if (isset($commandRequests[$request])) { - $e->addFailedCommand($commandRequests[$request]); - unset($commandRequests[$request]); - } - } - - // Always emit the command after_send events for successful commands - foreach ($commandRequests as $success) { - $e->addSuccessfulCommand($commandRequests[$success]); - $this->dispatch('command.after_send', array('command' => $commandRequests[$success])); - } - - throw $e; - } - } - - protected function getResourceIteratorFactory() - { - if (!$this->resourceIteratorFactory) { - // Build the default resource iterator factory if one is not set - $clientClass = get_class($this); - $prefix = substr($clientClass, 0, strrpos($clientClass, '\\')); - $this->resourceIteratorFactory = new ResourceIteratorClassFactory(array( - "{$prefix}\\Iterator", - "{$prefix}\\Model" - )); - } - - return $this->resourceIteratorFactory; - } - - /** - * Get the command factory associated with the client - * - * @return CommandFactoryInterface - */ - protected function getCommandFactory() - { - if (!$this->commandFactory) { - $this->commandFactory = CompositeFactory::getDefaultChain($this); - } - - return $this->commandFactory; - } - - /** - * @deprecated - * @codeCoverageIgnore - */ - public function enableMagicMethods($isEnabled) - { - Version::warn(__METHOD__ . ' is deprecated'); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php deleted file mode 100644 index 814154f00..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php +++ /dev/null @@ -1,68 +0,0 @@ -operation = $operation ?: $this->createOperation(); - foreach ($this->operation->getParams() as $name => $arg) { - $currentValue = $this[$name]; - $configValue = $arg->getValue($currentValue); - // If default or static values are set, then this should always be updated on the config object - if ($currentValue !== $configValue) { - $this[$name] = $configValue; - } - } - - $headers = $this[self::HEADERS_OPTION]; - if (!$headers instanceof Collection) { - $this[self::HEADERS_OPTION] = new Collection((array) $headers); - } - - // You can set a command.on_complete option in your parameters to set an onComplete callback - if ($onComplete = $this['command.on_complete']) { - unset($this['command.on_complete']); - $this->setOnComplete($onComplete); - } - - // Set the hidden additional parameters - if (!$this[self::HIDDEN_PARAMS]) { - $this[self::HIDDEN_PARAMS] = array( - self::HEADERS_OPTION, - self::RESPONSE_PROCESSING, - self::HIDDEN_PARAMS, - self::REQUEST_OPTIONS - ); - } - - $this->init(); - } - - /** - * Custom clone behavior - */ - public function __clone() - { - $this->request = null; - $this->result = null; - } - - /** - * Execute the command in the same manner as calling a function - * - * @return mixed Returns the result of {@see AbstractCommand::execute} - */ - public function __invoke() - { - return $this->execute(); - } - - public function getName() - { - return $this->operation->getName(); - } - - /** - * Get the API command information about the command - * - * @return OperationInterface - */ - public function getOperation() - { - return $this->operation; - } - - public function setOnComplete($callable) - { - if (!is_callable($callable)) { - throw new InvalidArgumentException('The onComplete function must be callable'); - } - - $this->onComplete = $callable; - - return $this; - } - - public function execute() - { - if (!$this->client) { - throw new CommandException('A client must be associated with the command before it can be executed.'); - } - - return $this->client->execute($this); - } - - public function getClient() - { - return $this->client; - } - - public function setClient(ClientInterface $client) - { - $this->client = $client; - - return $this; - } - - public function getRequest() - { - if (!$this->request) { - throw new CommandException('The command must be prepared before retrieving the request'); - } - - return $this->request; - } - - public function getResponse() - { - if (!$this->isExecuted()) { - $this->execute(); - } - - return $this->request->getResponse(); - } - - public function getResult() - { - if (!$this->isExecuted()) { - $this->execute(); - } - - if (null === $this->result) { - $this->process(); - // Call the onComplete method if one is set - if ($this->onComplete) { - call_user_func($this->onComplete, $this); - } - } - - return $this->result; - } - - public function setResult($result) - { - $this->result = $result; - - return $this; - } - - public function isPrepared() - { - return $this->request !== null; - } - - public function isExecuted() - { - return $this->request !== null && $this->request->getState() == 'complete'; - } - - public function prepare() - { - if (!$this->isPrepared()) { - if (!$this->client) { - throw new CommandException('A client must be associated with the command before it can be prepared.'); - } - - // If no response processing value was specified, then attempt to use the highest level of processing - if (!isset($this[self::RESPONSE_PROCESSING])) { - $this[self::RESPONSE_PROCESSING] = self::TYPE_MODEL; - } - - // Notify subscribers of the client that the command is being prepared - $this->client->dispatch('command.before_prepare', array('command' => $this)); - - // Fail on missing required arguments, and change parameters via filters - $this->validate(); - // Delegate to the subclass that implements the build method - $this->build(); - - // Add custom request headers set on the command - if ($headers = $this[self::HEADERS_OPTION]) { - foreach ($headers as $key => $value) { - $this->request->setHeader($key, $value); - } - } - - // Add any curl options to the request - if ($options = $this[Client::CURL_OPTIONS]) { - $this->request->getCurlOptions()->overwriteWith(CurlHandle::parseCurlConfig($options)); - } - - // Set a custom response body - if ($responseBody = $this[self::RESPONSE_BODY]) { - $this->request->setResponseBody($responseBody); - } - - $this->client->dispatch('command.after_prepare', array('command' => $this)); - } - - return $this->request; - } - - /** - * Set the validator used to validate and prepare command parameters and nested JSON schemas. If no validator is - * set, then the command will validate using the default {@see SchemaValidator}. - * - * @param ValidatorInterface $validator Validator used to prepare and validate properties against a JSON schema - * - * @return self - */ - public function setValidator(ValidatorInterface $validator) - { - $this->validator = $validator; - - return $this; - } - - public function getRequestHeaders() - { - return $this[self::HEADERS_OPTION]; - } - - /** - * Initialize the command (hook that can be implemented in subclasses) - */ - protected function init() {} - - /** - * Create the request object that will carry out the command - */ - abstract protected function build(); - - /** - * Hook used to create an operation for concrete commands that are not associated with a service description - * - * @return OperationInterface - */ - protected function createOperation() - { - return new Operation(array('name' => get_class($this))); - } - - /** - * Create the result of the command after the request has been completed. - * Override this method in subclasses to customize this behavior - */ - protected function process() - { - $this->result = $this[self::RESPONSE_PROCESSING] != self::TYPE_RAW - ? DefaultResponseParser::getInstance()->parse($this) - : $this->request->getResponse(); - } - - /** - * Validate and prepare the command based on the schema and rules defined by the command's Operation object - * - * @throws ValidationException when validation errors occur - */ - protected function validate() - { - // Do not perform request validation/transformation if it is disable - if ($this[self::DISABLE_VALIDATION]) { - return; - } - - $errors = array(); - $validator = $this->getValidator(); - foreach ($this->operation->getParams() as $name => $schema) { - $value = $this[$name]; - if (!$validator->validate($schema, $value)) { - $errors = array_merge($errors, $validator->getErrors()); - } elseif ($value !== $this[$name]) { - // Update the config value if it changed and no validation errors were encountered - $this->data[$name] = $value; - } - } - - // Validate additional parameters - $hidden = $this[self::HIDDEN_PARAMS]; - - if ($properties = $this->operation->getAdditionalParameters()) { - foreach ($this->toArray() as $name => $value) { - // It's only additional if it isn't defined in the schema - if (!$this->operation->hasParam($name) && !in_array($name, $hidden)) { - // Always set the name so that error messages are useful - $properties->setName($name); - if (!$validator->validate($properties, $value)) { - $errors = array_merge($errors, $validator->getErrors()); - } elseif ($value !== $this[$name]) { - $this->data[$name] = $value; - } - } - } - } - - if (!empty($errors)) { - $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); - $e->setErrors($errors); - throw $e; - } - } - - /** - * Get the validator used to prepare and validate properties. If no validator has been set on the command, then - * the default {@see SchemaValidator} will be used. - * - * @return ValidatorInterface - */ - protected function getValidator() - { - if (!$this->validator) { - $this->validator = SchemaValidator::getInstance(); - } - - return $this->validator; - } - - /** - * Get array of any validation errors - * If no validator has been set then return false - */ - public function getValidationErrors() - { - if (!$this->validator) { - return false; - } - - return $this->validator->getErrors(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php deleted file mode 100644 index cb6ac40ce..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php +++ /dev/null @@ -1,41 +0,0 @@ -request = $closure($this, $this->operation); - - if (!$this->request || !$this->request instanceof RequestInterface) { - throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php deleted file mode 100644 index fbb61d2ff..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php +++ /dev/null @@ -1,128 +0,0 @@ -stopPropagation(); - } - - /** - * Get the created object - * - * @return mixed - */ - public function getResult() - { - return $this['result']; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php deleted file mode 100644 index 2dc4acd37..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultRequestSerializer.php +++ /dev/null @@ -1,169 +0,0 @@ -factory = $factory; - } - - /** - * Add a location visitor to the serializer - * - * @param string $location Location to associate with the visitor - * @param RequestVisitorInterface $visitor Visitor to attach - * - * @return self - */ - public function addVisitor($location, RequestVisitorInterface $visitor) - { - $this->factory->addRequestVisitor($location, $visitor); - - return $this; - } - - public function prepare(CommandInterface $command) - { - $request = $this->createRequest($command); - // Keep an array of visitors found in the operation - $foundVisitors = array(); - $operation = $command->getOperation(); - - // Add arguments to the request using the location attribute - foreach ($operation->getParams() as $name => $arg) { - /** @var $arg \Guzzle\Service\Description\Parameter */ - $location = $arg->getLocation(); - // Skip 'uri' locations because they've already been processed - if ($location && $location != 'uri') { - // Instantiate visitors as they are detected in the properties - if (!isset($foundVisitors[$location])) { - $foundVisitors[$location] = $this->factory->getRequestVisitor($location); - } - // Ensure that a value has been set for this parameter - $value = $command[$name]; - if ($value !== null) { - // Apply the parameter value with the location visitor - $foundVisitors[$location]->visit($command, $request, $arg, $value); - } - } - } - - // Serialize additional parameters - if ($additional = $operation->getAdditionalParameters()) { - if ($visitor = $this->prepareAdditionalParameters($operation, $command, $request, $additional)) { - $foundVisitors[$additional->getLocation()] = $visitor; - } - } - - // Call the after method on each visitor found in the operation - foreach ($foundVisitors as $visitor) { - $visitor->after($command, $request); - } - - return $request; - } - - /** - * Serialize additional parameters - * - * @param OperationInterface $operation Operation that owns the command - * @param CommandInterface $command Command to prepare - * @param RequestInterface $request Request to serialize - * @param Parameter $additional Additional parameters - * - * @return null|RequestVisitorInterface - */ - protected function prepareAdditionalParameters( - OperationInterface $operation, - CommandInterface $command, - RequestInterface $request, - Parameter $additional - ) { - if (!($location = $additional->getLocation())) { - return; - } - - $visitor = $this->factory->getRequestVisitor($location); - $hidden = $command[$command::HIDDEN_PARAMS]; - - foreach ($command->toArray() as $key => $value) { - // Ignore values that are null or built-in command options - if ($value !== null - && !in_array($key, $hidden) - && !$operation->hasParam($key) - ) { - $additional->setName($key); - $visitor->visit($command, $request, $additional, $value); - } - } - - return $visitor; - } - - /** - * Create a request for the command and operation - * - * @param CommandInterface $command Command to create a request for - * - * @return RequestInterface - */ - protected function createRequest(CommandInterface $command) - { - $operation = $command->getOperation(); - $client = $command->getClient(); - $options = $command[AbstractCommand::REQUEST_OPTIONS] ?: array(); - - // If the command does not specify a template, then assume the base URL of the client - if (!($uri = $operation->getUri())) { - return $client->createRequest($operation->getHttpMethod(), $client->getBaseUrl(), null, null, $options); - } - - // Get the path values and use the client config settings - $variables = array(); - foreach ($operation->getParams() as $name => $arg) { - if ($arg->getLocation() == 'uri') { - if (isset($command[$name])) { - $variables[$name] = $arg->filter($command[$name]); - if (!is_array($variables[$name])) { - $variables[$name] = (string) $variables[$name]; - } - } - } - } - - return $client->createRequest($operation->getHttpMethod(), array($uri, $variables), null, null, $options); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php deleted file mode 100644 index 4fe380376..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/DefaultResponseParser.php +++ /dev/null @@ -1,55 +0,0 @@ -getRequest()->getResponse(); - - // Account for hard coded content-type values specified in service descriptions - if ($contentType = $command['command.expects']) { - $response->setHeader('Content-Type', $contentType); - } else { - $contentType = (string) $response->getHeader('Content-Type'); - } - - return $this->handleParsing($command, $response, $contentType); - } - - protected function handleParsing(CommandInterface $command, Response $response, $contentType) - { - $result = $response; - if ($result->getBody()) { - if (stripos($contentType, 'json') !== false) { - $result = $result->json(); - } elseif (stripos($contentType, 'xml') !== false) { - $result = $result->xml(); - } - } - - return $result; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php deleted file mode 100644 index 1c5ce0741..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php +++ /dev/null @@ -1,39 +0,0 @@ -client = $client; - $this->aliases = $aliases; - } - - public function factory($name, array $args = array()) - { - if (isset($this->aliases[$name])) { - try { - return $this->client->getCommand($this->aliases[$name], $args); - } catch (InvalidArgumentException $e) { - return null; - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php deleted file mode 100644 index 8c46983d6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php +++ /dev/null @@ -1,154 +0,0 @@ -getDescription()) { - $factories[] = new ServiceDescriptionFactory($description); - } - $factories[] = new ConcreteClassFactory($client); - - return new self($factories); - } - - /** - * @param array $factories Array of command factories - */ - public function __construct(array $factories = array()) - { - $this->factories = $factories; - } - - /** - * Add a command factory to the chain - * - * @param FactoryInterface $factory Factory to add - * @param string|FactoryInterface $before Insert the new command factory before a command factory class or object - * matching a class name. - * @return CompositeFactory - */ - public function add(FactoryInterface $factory, $before = null) - { - $pos = null; - - if ($before) { - foreach ($this->factories as $i => $f) { - if ($before instanceof FactoryInterface) { - if ($f === $before) { - $pos = $i; - break; - } - } elseif (is_string($before)) { - if ($f instanceof $before) { - $pos = $i; - break; - } - } - } - } - - if ($pos === null) { - $this->factories[] = $factory; - } else { - array_splice($this->factories, $i, 0, array($factory)); - } - - return $this; - } - - /** - * Check if the chain contains a specific command factory - * - * @param FactoryInterface|string $factory Factory to check - * - * @return bool - */ - public function has($factory) - { - return (bool) $this->find($factory); - } - - /** - * Remove a specific command factory from the chain - * - * @param string|FactoryInterface $factory Factory to remove by name or instance - * - * @return CompositeFactory - */ - public function remove($factory = null) - { - if (!($factory instanceof FactoryInterface)) { - $factory = $this->find($factory); - } - - $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { - return $f !== $factory; - })); - - return $this; - } - - /** - * Get a command factory by class name - * - * @param string|FactoryInterface $factory Command factory class or instance - * - * @return null|FactoryInterface - */ - public function find($factory) - { - foreach ($this->factories as $f) { - if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { - return $f; - } - } - } - - /** - * Create a command using the associated command factories - * - * @param string $name Name of the command - * @param array $args Command arguments - * - * @return CommandInterface - */ - public function factory($name, array $args = array()) - { - foreach ($this->factories as $factory) { - $command = $factory->factory($name, $args); - if ($command) { - return $command; - } - } - } - - public function count() - { - return count($this->factories); - } - - public function getIterator() - { - return new \ArrayIterator($this->factories); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php deleted file mode 100644 index 0e93deaa0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php +++ /dev/null @@ -1,47 +0,0 @@ -client = $client; - $this->inflector = $inflector ?: Inflector::getDefault(); - } - - public function factory($name, array $args = array()) - { - // Determine the class to instantiate based on the namespace of the current client and the default directory - $prefix = $this->client->getConfig('command.prefix'); - if (!$prefix) { - // The prefix can be specified in a factory method and is cached - $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; - $this->client->getConfig()->set('command.prefix', $prefix); - } - - $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); - - // Create the concrete command if it exists - if (class_exists($class)) { - return new $class($args); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php deleted file mode 100644 index 35c299d9d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -map = $map; - } - - public function factory($name, array $args = array()) - { - if (isset($this->map[$name])) { - $class = $this->map[$name]; - - return new $class($args); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php deleted file mode 100644 index b943a5b50..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php +++ /dev/null @@ -1,71 +0,0 @@ -setServiceDescription($description); - $this->inflector = $inflector; - } - - /** - * Change the service description used with the factory - * - * @param ServiceDescriptionInterface $description Service description to use - * - * @return FactoryInterface - */ - public function setServiceDescription(ServiceDescriptionInterface $description) - { - $this->description = $description; - - return $this; - } - - /** - * Returns the service description - * - * @return ServiceDescriptionInterface - */ - public function getServiceDescription() - { - return $this->description; - } - - public function factory($name, array $args = array()) - { - $command = $this->description->getOperation($name); - - // If a command wasn't found, then try to uppercase the first letter and try again - if (!$command) { - $command = $this->description->getOperation(ucfirst($name)); - // If an inflector was passed, then attempt to get the command using snake_case inflection - if (!$command && $this->inflector) { - $command = $this->description->getOperation($this->inflector->snake($name)); - } - } - - if ($command) { - $class = $command->getClass(); - return new $class($args, $command, $this->description); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php deleted file mode 100644 index adcfca1ba..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/AbstractRequestVisitor.php +++ /dev/null @@ -1,69 +0,0 @@ -resolveRecursively($value, $param) - : $param->filter($value); - } - - /** - * Map nested parameters into the location_key based parameters - * - * @param array $value Value to map - * @param Parameter $param Parameter that holds information about the current key - * - * @return array Returns the mapped array - */ - protected function resolveRecursively(array $value, Parameter $param) - { - foreach ($value as $name => &$v) { - switch ($param->getType()) { - case 'object': - if ($subParam = $param->getProperty($name)) { - $key = $subParam->getWireName(); - $value[$key] = $this->prepareValue($v, $subParam); - if ($name != $key) { - unset($value[$name]); - } - } elseif ($param->getAdditionalProperties() instanceof Parameter) { - $v = $this->prepareValue($v, $param->getAdditionalProperties()); - } - break; - case 'array': - if ($items = $param->getItems()) { - $v = $this->prepareValue($v, $items); - } - break; - } - } - - return $param->filter($value); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php deleted file mode 100644 index 168d7806f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/BodyVisitor.php +++ /dev/null @@ -1,58 +0,0 @@ -filter($value); - $entityBody = EntityBody::factory($value); - $request->setBody($entityBody); - $this->addExpectHeader($request, $entityBody, $param->getData('expect_header')); - // Add the Content-Encoding header if one is set on the EntityBody - if ($encoding = $entityBody->getContentEncoding()) { - $request->setHeader('Content-Encoding', $encoding); - } - } - - /** - * Add the appropriate expect header to a request - * - * @param EntityEnclosingRequestInterface $request Request to update - * @param EntityBodyInterface $body Entity body of the request - * @param string|int $expect Expect header setting - */ - protected function addExpectHeader(EntityEnclosingRequestInterface $request, EntityBodyInterface $body, $expect) - { - // Allow the `expect` data parameter to be set to remove the Expect header from the request - if ($expect === false) { - $request->removeHeader('Expect'); - } elseif ($expect !== true) { - // Default to using a MB as the point in which to start using the expect header - $expect = $expect ?: 1048576; - // If the expect_header value is numeric then only add if the size is greater than the cutoff - if (is_numeric($expect) && $body->getSize()) { - if ($body->getSize() < $expect) { - $request->removeHeader('Expect'); - } else { - $request->setHeader('Expect', '100-Continue'); - } - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php deleted file mode 100644 index 2a537542c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/HeaderVisitor.php +++ /dev/null @@ -1,44 +0,0 @@ -filter($value); - if ($param->getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { - $this->addPrefixedHeaders($request, $param, $value); - } else { - $request->setHeader($param->getWireName(), $value); - } - } - - /** - * Add a prefixed array of headers to the request - * - * @param RequestInterface $request Request to update - * @param Parameter $param Parameter object - * @param array $value Header array to add - * - * @throws InvalidArgumentException - */ - protected function addPrefixedHeaders(RequestInterface $request, Parameter $param, $value) - { - if (!is_array($value)) { - throw new InvalidArgumentException('An array of mapped headers expected, but received a single value'); - } - $prefix = $param->getSentAs(); - foreach ($value as $headerName => $headerValue) { - $request->setHeader($prefix . $headerName, $headerValue); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php deleted file mode 100644 index 757e1c520..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/JsonVisitor.php +++ /dev/null @@ -1,63 +0,0 @@ -data = new \SplObjectStorage(); - } - - /** - * Set the Content-Type header to add to the request if JSON is added to the body. This visitor does not add a - * Content-Type header unless you specify one here. - * - * @param string $header Header to set when JSON is added (e.g. application/json) - * - * @return self - */ - public function setContentTypeHeader($header = 'application/json') - { - $this->jsonContentType = $header; - - return $this; - } - - public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) - { - if (isset($this->data[$command])) { - $json = $this->data[$command]; - } else { - $json = array(); - } - $json[$param->getWireName()] = $this->prepareValue($value, $param); - $this->data[$command] = $json; - } - - public function after(CommandInterface $command, RequestInterface $request) - { - if (isset($this->data[$command])) { - // Don't overwrite the Content-Type if one is set - if ($this->jsonContentType && !$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', $this->jsonContentType); - } - - $request->setBody(json_encode($this->data[$command])); - unset($this->data[$command]); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php deleted file mode 100644 index 975850b74..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFieldVisitor.php +++ /dev/null @@ -1,18 +0,0 @@ -setPostField($param->getWireName(), $this->prepareValue($value, $param)); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php deleted file mode 100644 index 0853ebe62..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/PostFileVisitor.php +++ /dev/null @@ -1,24 +0,0 @@ -filter($value); - if ($value instanceof PostFileInterface) { - $request->addPostFile($value); - } else { - $request->addPostFile($param->getWireName(), $value); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php deleted file mode 100644 index 315877aa0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/QueryVisitor.php +++ /dev/null @@ -1,18 +0,0 @@ -getQuery()->set($param->getWireName(), $this->prepareValue($value, $param)); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php deleted file mode 100644 index 14e0b2d2b..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/RequestVisitorInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -setResponseBody($value); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php deleted file mode 100644 index 5b7148787..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Request/XmlVisitor.php +++ /dev/null @@ -1,252 +0,0 @@ -data = new \SplObjectStorage(); - } - - /** - * Change the content-type header that is added when XML is found - * - * @param string $header Header to set when XML is found - * - * @return self - */ - public function setContentTypeHeader($header) - { - $this->contentType = $header; - - return $this; - } - - public function visit(CommandInterface $command, RequestInterface $request, Parameter $param, $value) - { - $xml = isset($this->data[$command]) - ? $this->data[$command] - : $this->createRootElement($param->getParent()); - $this->addXml($xml, $param, $value); - - $this->data[$command] = $xml; - } - - public function after(CommandInterface $command, RequestInterface $request) - { - $xml = null; - - // If data was found that needs to be serialized, then do so - if (isset($this->data[$command])) { - $xml = $this->finishDocument($this->data[$command]); - unset($this->data[$command]); - } else { - // Check if XML should always be sent for the command - $operation = $command->getOperation(); - if ($operation->getData('xmlAllowEmpty')) { - $xmlWriter = $this->createRootElement($operation); - $xml = $this->finishDocument($xmlWriter); - } - } - - if ($xml) { - // Don't overwrite the Content-Type if one is set - if ($this->contentType && !$request->hasHeader('Content-Type')) { - $request->setHeader('Content-Type', $this->contentType); - } - $request->setBody($xml); - } - } - - /** - * Create the root XML element to use with a request - * - * @param Operation $operation Operation object - * - * @return \XMLWriter - */ - protected function createRootElement(Operation $operation) - { - static $defaultRoot = array('name' => 'Request'); - // If no root element was specified, then just wrap the XML in 'Request' - $root = $operation->getData('xmlRoot') ?: $defaultRoot; - // Allow the XML declaration to be customized with xmlEncoding - $encoding = $operation->getData('xmlEncoding'); - - $xmlWriter = $this->startDocument($encoding); - - $xmlWriter->startElement($root['name']); - // Create the wrapping element with no namespaces if no namespaces were present - if (!empty($root['namespaces'])) { - // Create the wrapping element with an array of one or more namespaces - foreach ((array) $root['namespaces'] as $prefix => $uri) { - $nsLabel = 'xmlns'; - if (!is_numeric($prefix)) { - $nsLabel .= ':'.$prefix; - } - $xmlWriter->writeAttribute($nsLabel, $uri); - } - } - return $xmlWriter; - } - - /** - * Recursively build the XML body - * - * @param \XMLWriter $xmlWriter XML to modify - * @param Parameter $param API Parameter - * @param mixed $value Value to add - */ - protected function addXml(\XMLWriter $xmlWriter, Parameter $param, $value) - { - if ($value === null) { - return; - } - - $value = $param->filter($value); - $type = $param->getType(); - $name = $param->getWireName(); - $prefix = null; - $namespace = $param->getData('xmlNamespace'); - if (false !== strpos($name, ':')) { - list($prefix, $name) = explode(':', $name, 2); - } - - if ($type == 'object' || $type == 'array') { - if (!$param->getData('xmlFlattened')) { - $xmlWriter->startElementNS(null, $name, $namespace); - } - if ($param->getType() == 'array') { - $this->addXmlArray($xmlWriter, $param, $value); - } elseif ($param->getType() == 'object') { - $this->addXmlObject($xmlWriter, $param, $value); - } - if (!$param->getData('xmlFlattened')) { - $xmlWriter->endElement(); - } - return; - } - if ($param->getData('xmlAttribute')) { - $this->writeAttribute($xmlWriter, $prefix, $name, $namespace, $value); - } else { - $this->writeElement($xmlWriter, $prefix, $name, $namespace, $value); - } - } - - /** - * Write an attribute with namespace if used - * - * @param \XMLWriter $xmlWriter XMLWriter instance - * @param string $prefix Namespace prefix if any - * @param string $name Attribute name - * @param string $namespace The uri of the namespace - * @param string $value The attribute content - */ - protected function writeAttribute($xmlWriter, $prefix, $name, $namespace, $value) - { - if (empty($namespace)) { - $xmlWriter->writeAttribute($name, $value); - } else { - $xmlWriter->writeAttributeNS($prefix, $name, $namespace, $value); - } - } - - /** - * Write an element with namespace if used - * - * @param \XMLWriter $xmlWriter XML writer resource - * @param string $prefix Namespace prefix if any - * @param string $name Element name - * @param string $namespace The uri of the namespace - * @param string $value The element content - */ - protected function writeElement(\XMLWriter $xmlWriter, $prefix, $name, $namespace, $value) - { - $xmlWriter->startElementNS($prefix, $name, $namespace); - if (strpbrk($value, '<>&')) { - $xmlWriter->writeCData($value); - } else { - $xmlWriter->writeRaw($value); - } - $xmlWriter->endElement(); - } - - /** - * Create a new xml writer and start a document - * - * @param string $encoding document encoding - * - * @return \XMLWriter the writer resource - */ - protected function startDocument($encoding) - { - $xmlWriter = new \XMLWriter(); - $xmlWriter->openMemory(); - $xmlWriter->startDocument('1.0', $encoding); - - return $xmlWriter; - } - - /** - * End the document and return the output - * - * @param \XMLWriter $xmlWriter - * - * @return \string the writer resource - */ - protected function finishDocument($xmlWriter) - { - $xmlWriter->endDocument(); - - return $xmlWriter->outputMemory(); - } - - /** - * Add an array to the XML - */ - protected function addXmlArray(\XMLWriter $xmlWriter, Parameter $param, &$value) - { - if ($items = $param->getItems()) { - foreach ($value as $v) { - $this->addXml($xmlWriter, $items, $v); - } - } - } - - /** - * Add an object to the XML - */ - protected function addXmlObject(\XMLWriter $xmlWriter, Parameter $param, &$value) - { - $noAttributes = array(); - // add values which have attributes - foreach ($value as $name => $v) { - if ($property = $param->getProperty($name)) { - if ($property->getData('xmlAttribute')) { - $this->addXml($xmlWriter, $property, $v); - } else { - $noAttributes[] = array('value' => $v, 'property' => $property); - } - } - } - // now add values with no attributes - foreach ($noAttributes as $element) { - $this->addXml($xmlWriter, $element['property'], $element['value']); - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php deleted file mode 100644 index d87eeb945..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/AbstractResponseVisitor.php +++ /dev/null @@ -1,26 +0,0 @@ -getName()] = $param->filter($response->getBody()); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php deleted file mode 100644 index 0f8737cbd..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/HeaderVisitor.php +++ /dev/null @@ -1,50 +0,0 @@ -getType() == 'object' && $param->getAdditionalProperties() instanceof Parameter) { - $this->processPrefixedHeaders($response, $param, $value); - } else { - $value[$param->getName()] = $param->filter((string) $response->getHeader($param->getWireName())); - } - } - - /** - * Process a prefixed header array - * - * @param Response $response Response that contains the headers - * @param Parameter $param Parameter object - * @param array $value Value response array to modify - */ - protected function processPrefixedHeaders(Response $response, Parameter $param, &$value) - { - // Grab prefixed headers that should be placed into an array with the prefix stripped - if ($prefix = $param->getSentAs()) { - $container = $param->getName(); - $len = strlen($prefix); - // Find all matching headers and place them into the containing element - foreach ($response->getHeaders()->toArray() as $key => $header) { - if (stripos($key, $prefix) === 0) { - // Account for multi-value headers - $value[$container][substr($key, $len)] = count($header) == 1 ? end($header) : $header; - } - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php deleted file mode 100644 index a609ebd8c..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/JsonVisitor.php +++ /dev/null @@ -1,93 +0,0 @@ -getResponse()->json(); - } - - public function visit( - CommandInterface $command, - Response $response, - Parameter $param, - &$value, - $context = null - ) { - $name = $param->getName(); - $key = $param->getWireName(); - if (isset($value[$key])) { - $this->recursiveProcess($param, $value[$key]); - if ($key != $name) { - $value[$name] = $value[$key]; - unset($value[$key]); - } - } - } - - /** - * Recursively process a parameter while applying filters - * - * @param Parameter $param API parameter being validated - * @param mixed $value Value to validate and process. The value may change during this process. - */ - protected function recursiveProcess(Parameter $param, &$value) - { - if ($value === null) { - return; - } - - if (is_array($value)) { - $type = $param->getType(); - if ($type == 'array') { - foreach ($value as &$item) { - $this->recursiveProcess($param->getItems(), $item); - } - } elseif ($type == 'object' && !isset($value[0])) { - // On the above line, we ensure that the array is associative and not numerically indexed - $knownProperties = array(); - if ($properties = $param->getProperties()) { - foreach ($properties as $property) { - $name = $property->getName(); - $key = $property->getWireName(); - $knownProperties[$name] = 1; - if (isset($value[$key])) { - $this->recursiveProcess($property, $value[$key]); - if ($key != $name) { - $value[$name] = $value[$key]; - unset($value[$key]); - } - } - } - } - - // Remove any unknown and potentially unsafe properties - if ($param->getAdditionalProperties() === false) { - $value = array_intersect_key($value, $knownProperties); - } elseif (($additional = $param->getAdditionalProperties()) !== true) { - // Validate and filter additional properties - foreach ($value as &$v) { - $this->recursiveProcess($additional, $v); - } - } - } - } - - $value = $param->filter($value); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php deleted file mode 100644 index 1b10ebce7..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ReasonPhraseVisitor.php +++ /dev/null @@ -1,23 +0,0 @@ -getName()] = $response->getReasonPhrase(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php deleted file mode 100644 index 033f40c3f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/ResponseVisitorInterface.php +++ /dev/null @@ -1,46 +0,0 @@ -getName()] = $response->getStatusCode(); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php deleted file mode 100644 index bb7124be7..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/Response/XmlVisitor.php +++ /dev/null @@ -1,151 +0,0 @@ -getResponse()->xml()), true); - } - - public function visit( - CommandInterface $command, - Response $response, - Parameter $param, - &$value, - $context = null - ) { - $sentAs = $param->getWireName(); - $name = $param->getName(); - if (isset($value[$sentAs])) { - $this->recursiveProcess($param, $value[$sentAs]); - if ($name != $sentAs) { - $value[$name] = $value[$sentAs]; - unset($value[$sentAs]); - } - } - } - - /** - * Recursively process a parameter while applying filters - * - * @param Parameter $param API parameter being processed - * @param mixed $value Value to validate and process. The value may change during this process. - */ - protected function recursiveProcess(Parameter $param, &$value) - { - $type = $param->getType(); - - if (!is_array($value)) { - if ($type == 'array') { - // Cast to an array if the value was a string, but should be an array - $this->recursiveProcess($param->getItems(), $value); - $value = array($value); - } - } elseif ($type == 'object') { - $this->processObject($param, $value); - } elseif ($type == 'array') { - $this->processArray($param, $value); - } elseif ($type == 'string' && gettype($value) == 'array') { - $value = ''; - } - - if ($value !== null) { - $value = $param->filter($value); - } - } - - /** - * Process an array - * - * @param Parameter $param API parameter being parsed - * @param mixed $value Value to process - */ - protected function processArray(Parameter $param, &$value) - { - // Convert the node if it was meant to be an array - if (!isset($value[0])) { - // Collections fo nodes are sometimes wrapped in an additional array. For example: - // 12 should become: - // array('Items' => array(array('a' => 1), array('a' => 2)) - // Some nodes are not wrapped. For example: 12 - // should become array('Foo' => array(array('a' => 1), array('a' => 2)) - if ($param->getItems() && isset($value[$param->getItems()->getWireName()])) { - // Account for the case of a collection wrapping wrapped nodes: Items => Item[] - $value = $value[$param->getItems()->getWireName()]; - // If the wrapped node only had one value, then make it an array of nodes - if (!isset($value[0]) || !is_array($value)) { - $value = array($value); - } - } elseif (!empty($value)) { - // Account for repeated nodes that must be an array: Foo => Baz, Foo => Baz, but only if the - // value is set and not empty - $value = array($value); - } - } - - foreach ($value as &$item) { - $this->recursiveProcess($param->getItems(), $item); - } - } - - /** - * Process an object - * - * @param Parameter $param API parameter being parsed - * @param mixed $value Value to process - */ - protected function processObject(Parameter $param, &$value) - { - // Ensure that the array is associative and not numerically indexed - if (!isset($value[0]) && ($properties = $param->getProperties())) { - $knownProperties = array(); - foreach ($properties as $property) { - $name = $property->getName(); - $sentAs = $property->getWireName(); - $knownProperties[$name] = 1; - if ($property->getData('xmlAttribute')) { - $this->processXmlAttribute($property, $value); - } elseif (isset($value[$sentAs])) { - $this->recursiveProcess($property, $value[$sentAs]); - if ($name != $sentAs) { - $value[$name] = $value[$sentAs]; - unset($value[$sentAs]); - } - } - } - - // Remove any unknown and potentially unsafe properties - if ($param->getAdditionalProperties() === false) { - $value = array_intersect_key($value, $knownProperties); - } - } - } - - /** - * Process an XML attribute property - * - * @param Parameter $property Property to process - * @param array $value Value to process and update - */ - protected function processXmlAttribute(Parameter $property, array &$value) - { - $sentAs = $property->getWireName(); - if (isset($value['@attributes'][$sentAs])) { - $value[$property->getName()] = $value['@attributes'][$sentAs]; - unset($value['@attributes'][$sentAs]); - if (empty($value['@attributes'])) { - unset($value['@attributes']); - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php deleted file mode 100644 index 74cb62813..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/LocationVisitor/VisitorFlyweight.php +++ /dev/null @@ -1,138 +0,0 @@ - 'Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor', - 'request.header' => 'Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor', - 'request.json' => 'Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', - 'request.postField' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor', - 'request.postFile' => 'Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor', - 'request.query' => 'Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor', - 'request.response_body' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', - 'request.responseBody' => 'Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor', - 'request.xml' => 'Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor', - 'response.body' => 'Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor', - 'response.header' => 'Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor', - 'response.json' => 'Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', - 'response.reasonPhrase' => 'Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor', - 'response.statusCode' => 'Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor', - 'response.xml' => 'Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor' - ); - - /** @var array Array of mappings of location names to classes */ - protected $mappings; - - /** @var array Cache of instantiated visitors */ - protected $cache = array(); - - /** - * @return self - * @codeCoverageIgnore - */ - public static function getInstance() - { - if (!self::$instance) { - self::$instance = new self(); - } - - return self::$instance; - } - - /** - * @param array $mappings Array mapping request.name and response.name to location visitor classes. Leave null to - * use the default values. - */ - public function __construct(array $mappings = null) - { - $this->mappings = $mappings === null ? self::$defaultMappings : $mappings; - } - - /** - * Get an instance of a request visitor by location name - * - * @param string $visitor Visitor name - * - * @return RequestVisitorInterface - */ - public function getRequestVisitor($visitor) - { - return $this->getKey('request.' . $visitor); - } - - /** - * Get an instance of a response visitor by location name - * - * @param string $visitor Visitor name - * - * @return ResponseVisitorInterface - */ - public function getResponseVisitor($visitor) - { - return $this->getKey('response.' . $visitor); - } - - /** - * Add a response visitor to the factory by name - * - * @param string $name Name of the visitor - * @param RequestVisitorInterface $visitor Visitor to add - * - * @return self - */ - public function addRequestVisitor($name, RequestVisitorInterface $visitor) - { - $this->cache['request.' . $name] = $visitor; - - return $this; - } - - /** - * Add a response visitor to the factory by name - * - * @param string $name Name of the visitor - * @param ResponseVisitorInterface $visitor Visitor to add - * - * @return self - */ - public function addResponseVisitor($name, ResponseVisitorInterface $visitor) - { - $this->cache['response.' . $name] = $visitor; - - return $this; - } - - /** - * Get a visitor by key value name - * - * @param string $key Key name to retrieve - * - * @return mixed - * @throws InvalidArgumentException - */ - private function getKey($key) - { - if (!isset($this->cache[$key])) { - if (!isset($this->mappings[$key])) { - list($type, $name) = explode('.', $key); - throw new InvalidArgumentException("No {$type} visitor has been mapped for {$name}"); - } - $this->cache[$key] = new $this->mappings[$key]; - } - - return $this->cache[$key]; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php deleted file mode 100644 index 0748b5af0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationCommand.php +++ /dev/null @@ -1,89 +0,0 @@ -responseParser = $parser; - - return $this; - } - - /** - * Set the request serializer used with the command - * - * @param RequestSerializerInterface $serializer Request serializer - * - * @return self - */ - public function setRequestSerializer(RequestSerializerInterface $serializer) - { - $this->requestSerializer = $serializer; - - return $this; - } - - /** - * Get the request serializer used with the command - * - * @return RequestSerializerInterface - */ - public function getRequestSerializer() - { - if (!$this->requestSerializer) { - // Use the default request serializer if none was found - $this->requestSerializer = DefaultRequestSerializer::getInstance(); - } - - return $this->requestSerializer; - } - - /** - * Get the response parser used for the operation - * - * @return ResponseParserInterface - */ - public function getResponseParser() - { - if (!$this->responseParser) { - // Use the default response parser if none was found - $this->responseParser = OperationResponseParser::getInstance(); - } - - return $this->responseParser; - } - - protected function build() - { - // Prepare and serialize the request - $this->request = $this->getRequestSerializer()->prepare($this); - } - - protected function process() - { - // Do not process the response if 'command.response_processing' is set to 'raw' - $this->result = $this[self::RESPONSE_PROCESSING] == self::TYPE_RAW - ? $this->request->getResponse() - : $this->getResponseParser()->parse($this); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php deleted file mode 100644 index ca00bc062..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/OperationResponseParser.php +++ /dev/null @@ -1,195 +0,0 @@ -factory = $factory; - $this->schemaInModels = $schemaInModels; - } - - /** - * Add a location visitor to the command - * - * @param string $location Location to associate with the visitor - * @param ResponseVisitorInterface $visitor Visitor to attach - * - * @return self - */ - public function addVisitor($location, ResponseVisitorInterface $visitor) - { - $this->factory->addResponseVisitor($location, $visitor); - - return $this; - } - - protected function handleParsing(CommandInterface $command, Response $response, $contentType) - { - $operation = $command->getOperation(); - $type = $operation->getResponseType(); - $model = null; - - if ($type == OperationInterface::TYPE_MODEL) { - $model = $operation->getServiceDescription()->getModel($operation->getResponseClass()); - } elseif ($type == OperationInterface::TYPE_CLASS) { - return $this->parseClass($command); - } - - if (!$model) { - // Return basic processing if the responseType is not model or the model cannot be found - return parent::handleParsing($command, $response, $contentType); - } elseif ($command[AbstractCommand::RESPONSE_PROCESSING] != AbstractCommand::TYPE_MODEL) { - // Returns a model with no visiting if the command response processing is not model - return new Model(parent::handleParsing($command, $response, $contentType)); - } else { - // Only inject the schema into the model if "schemaInModel" is true - return new Model($this->visitResult($model, $command, $response), $this->schemaInModels ? $model : null); - } - } - - /** - * Parse a class object - * - * @param CommandInterface $command Command to parse into an object - * - * @return mixed - * @throws ResponseClassException - */ - protected function parseClass(CommandInterface $command) - { - // Emit the operation.parse_class event. If a listener injects a 'result' property, then that will be the result - $event = new CreateResponseClassEvent(array('command' => $command)); - $command->getClient()->getEventDispatcher()->dispatch('command.parse_response', $event); - if ($result = $event->getResult()) { - return $result; - } - - $className = $command->getOperation()->getResponseClass(); - if (!method_exists($className, 'fromCommand')) { - throw new ResponseClassException("{$className} must exist and implement a static fromCommand() method"); - } - - return $className::fromCommand($command); - } - - /** - * Perform transformations on the result array - * - * @param Parameter $model Model that defines the structure - * @param CommandInterface $command Command that performed the operation - * @param Response $response Response received - * - * @return array Returns the array of result data - */ - protected function visitResult(Parameter $model, CommandInterface $command, Response $response) - { - $foundVisitors = $result = $knownProps = array(); - $props = $model->getProperties(); - - foreach ($props as $schema) { - if ($location = $schema->getLocation()) { - // Trigger the before method on the first found visitor of this type - if (!isset($foundVisitors[$location])) { - $foundVisitors[$location] = $this->factory->getResponseVisitor($location); - $foundVisitors[$location]->before($command, $result); - } - } - } - - // Visit additional properties when it is an actual schema - if (($additional = $model->getAdditionalProperties()) instanceof Parameter) { - $this->visitAdditionalProperties($model, $command, $response, $additional, $result, $foundVisitors); - } - - // Apply the parameter value with the location visitor - foreach ($props as $schema) { - $knownProps[$schema->getName()] = 1; - if ($location = $schema->getLocation()) { - $foundVisitors[$location]->visit($command, $response, $schema, $result); - } - } - - // Remove any unknown and potentially unsafe top-level properties - if ($additional === false) { - $result = array_intersect_key($result, $knownProps); - } - - // Call the after() method of each found visitor - foreach ($foundVisitors as $visitor) { - $visitor->after($command); - } - - return $result; - } - - protected function visitAdditionalProperties( - Parameter $model, - CommandInterface $command, - Response $response, - Parameter $additional, - &$result, - array &$foundVisitors - ) { - // Only visit when a location is specified - if ($location = $additional->getLocation()) { - if (!isset($foundVisitors[$location])) { - $foundVisitors[$location] = $this->factory->getResponseVisitor($location); - $foundVisitors[$location]->before($command, $result); - } - // Only traverse if an array was parsed from the before() visitors - if (is_array($result)) { - // Find each additional property - foreach (array_keys($result) as $key) { - // Check if the model actually knows this property. If so, then it is not additional - if (!$model->getProperty($key)) { - // Set the name to the key so that we can parse it with each visitor - $additional->setName($key); - $foundVisitors[$location]->visit($command, $response, $additional, $result); - } - } - // Reset the additionalProperties name to null - $additional->setName(null); - } - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php deleted file mode 100644 index 60b9334d4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Command/RequestSerializerInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - true, 'httpMethod' => true, 'uri' => true, 'class' => true, 'responseClass' => true, - 'responseType' => true, 'responseNotes' => true, 'notes' => true, 'summary' => true, 'documentationUrl' => true, - 'deprecated' => true, 'data' => true, 'parameters' => true, 'additionalParameters' => true, - 'errorResponses' => true - ); - - /** @var array Parameters */ - protected $parameters = array(); - - /** @var Parameter Additional parameters schema */ - protected $additionalParameters; - - /** @var string Name of the command */ - protected $name; - - /** @var string HTTP method */ - protected $httpMethod; - - /** @var string This is a short summary of what the operation does */ - protected $summary; - - /** @var string A longer text field to explain the behavior of the operation. */ - protected $notes; - - /** @var string Reference URL providing more information about the operation */ - protected $documentationUrl; - - /** @var string HTTP URI of the command */ - protected $uri; - - /** @var string Class of the command object */ - protected $class; - - /** @var string This is what is returned from the method */ - protected $responseClass; - - /** @var string Type information about the response */ - protected $responseType; - - /** @var string Information about the response returned by the operation */ - protected $responseNotes; - - /** @var bool Whether or not the command is deprecated */ - protected $deprecated; - - /** @var array Array of errors that could occur when running the command */ - protected $errorResponses; - - /** @var ServiceDescriptionInterface */ - protected $description; - - /** @var array Extra operation information */ - protected $data; - - /** - * Builds an Operation object using an array of configuration data: - * - name: (string) Name of the command - * - httpMethod: (string) HTTP method of the operation - * - uri: (string) URI template that can create a relative or absolute URL - * - class: (string) Concrete class that implements this command - * - parameters: (array) Associative array of parameters for the command. {@see Parameter} for information. - * - summary: (string) This is a short summary of what the operation does - * - notes: (string) A longer text field to explain the behavior of the operation. - * - documentationUrl: (string) Reference URL providing more information about the operation - * - responseClass: (string) This is what is returned from the method. Can be a primitive, PSR-0 compliant - * class name, or model. - * - responseNotes: (string) Information about the response returned by the operation - * - responseType: (string) One of 'primitive', 'class', 'model', or 'documentation'. If not specified, this - * value will be automatically inferred based on whether or not there is a model matching the - * name, if a matching PSR-0 compliant class name is found, or set to 'primitive' by default. - * - deprecated: (bool) Set to true if this is a deprecated command - * - errorResponses: (array) Errors that could occur when executing the command. Array of hashes, each with a - * 'code' (the HTTP response code), 'phrase' (response reason phrase or description of the - * error), and 'class' (a custom exception class that would be thrown if the error is - * encountered). - * - data: (array) Any extra data that might be used to help build or serialize the operation - * - additionalParameters: (null|array) Parameter schema to use when an option is passed to the operation that is - * not in the schema - * - * @param array $config Array of configuration data - * @param ServiceDescriptionInterface $description Service description used to resolve models if $ref tags are found - */ - public function __construct(array $config = array(), ServiceDescriptionInterface $description = null) - { - $this->description = $description; - - // Get the intersection of the available properties and properties set on the operation - foreach (array_intersect_key($config, self::$properties) as $key => $value) { - $this->{$key} = $value; - } - - $this->class = $this->class ?: self::DEFAULT_COMMAND_CLASS; - $this->deprecated = (bool) $this->deprecated; - $this->errorResponses = $this->errorResponses ?: array(); - $this->data = $this->data ?: array(); - - if (!$this->responseClass) { - $this->responseClass = 'array'; - $this->responseType = 'primitive'; - } elseif ($this->responseType) { - // Set the response type to perform validation - $this->setResponseType($this->responseType); - } else { - // A response class was set and no response type was set, so guess what the type is - $this->inferResponseType(); - } - - // Parameters need special handling when adding - if ($this->parameters) { - foreach ($this->parameters as $name => $param) { - if ($param instanceof Parameter) { - $param->setName($name)->setParent($this); - } elseif (is_array($param)) { - $param['name'] = $name; - $this->addParam(new Parameter($param, $this->description)); - } - } - } - - if ($this->additionalParameters) { - if ($this->additionalParameters instanceof Parameter) { - $this->additionalParameters->setParent($this); - } elseif (is_array($this->additionalParameters)) { - $this->setadditionalParameters(new Parameter($this->additionalParameters, $this->description)); - } - } - } - - public function toArray() - { - $result = array(); - // Grab valid properties and filter out values that weren't set - foreach (array_keys(self::$properties) as $check) { - if ($value = $this->{$check}) { - $result[$check] = $value; - } - } - // Remove the name property - unset($result['name']); - // Parameters need to be converted to arrays - $result['parameters'] = array(); - foreach ($this->parameters as $key => $param) { - $result['parameters'][$key] = $param->toArray(); - } - // Additional parameters need to be cast to an array - if ($this->additionalParameters instanceof Parameter) { - $result['additionalParameters'] = $this->additionalParameters->toArray(); - } - - return $result; - } - - public function getServiceDescription() - { - return $this->description; - } - - public function setServiceDescription(ServiceDescriptionInterface $description) - { - $this->description = $description; - - return $this; - } - - public function getParams() - { - return $this->parameters; - } - - public function getParamNames() - { - return array_keys($this->parameters); - } - - public function hasParam($name) - { - return isset($this->parameters[$name]); - } - - public function getParam($param) - { - return isset($this->parameters[$param]) ? $this->parameters[$param] : null; - } - - /** - * Add a parameter to the command - * - * @param Parameter $param Parameter to add - * - * @return self - */ - public function addParam(Parameter $param) - { - $this->parameters[$param->getName()] = $param; - $param->setParent($this); - - return $this; - } - - /** - * Remove a parameter from the command - * - * @param string $name Name of the parameter to remove - * - * @return self - */ - public function removeParam($name) - { - unset($this->parameters[$name]); - - return $this; - } - - public function getHttpMethod() - { - return $this->httpMethod; - } - - /** - * Set the HTTP method of the command - * - * @param string $httpMethod Method to set - * - * @return self - */ - public function setHttpMethod($httpMethod) - { - $this->httpMethod = $httpMethod; - - return $this; - } - - public function getClass() - { - return $this->class; - } - - /** - * Set the concrete class of the command - * - * @param string $className Concrete class name - * - * @return self - */ - public function setClass($className) - { - $this->class = $className; - - return $this; - } - - public function getName() - { - return $this->name; - } - - /** - * Set the name of the command - * - * @param string $name Name of the command - * - * @return self - */ - public function setName($name) - { - $this->name = $name; - - return $this; - } - - public function getSummary() - { - return $this->summary; - } - - /** - * Set a short summary of what the operation does - * - * @param string $summary Short summary of the operation - * - * @return self - */ - public function setSummary($summary) - { - $this->summary = $summary; - - return $this; - } - - public function getNotes() - { - return $this->notes; - } - - /** - * Set a longer text field to explain the behavior of the operation. - * - * @param string $notes Notes on the operation - * - * @return self - */ - public function setNotes($notes) - { - $this->notes = $notes; - - return $this; - } - - public function getDocumentationUrl() - { - return $this->documentationUrl; - } - - /** - * Set the URL pointing to additional documentation on the command - * - * @param string $docUrl Documentation URL - * - * @return self - */ - public function setDocumentationUrl($docUrl) - { - $this->documentationUrl = $docUrl; - - return $this; - } - - public function getResponseClass() - { - return $this->responseClass; - } - - /** - * Set what is returned from the method. Can be a primitive, class name, or model. For example: 'array', - * 'Guzzle\\Foo\\Baz', or 'MyModelName' (to reference a model by ID). - * - * @param string $responseClass Type of response - * - * @return self - */ - public function setResponseClass($responseClass) - { - $this->responseClass = $responseClass; - $this->inferResponseType(); - - return $this; - } - - public function getResponseType() - { - return $this->responseType; - } - - /** - * Set qualifying information about the responseClass. One of 'primitive', 'class', 'model', or 'documentation' - * - * @param string $responseType Response type information - * - * @return self - * @throws InvalidArgumentException - */ - public function setResponseType($responseType) - { - static $types = array( - self::TYPE_PRIMITIVE => true, - self::TYPE_CLASS => true, - self::TYPE_MODEL => true, - self::TYPE_DOCUMENTATION => true - ); - if (!isset($types[$responseType])) { - throw new InvalidArgumentException('responseType must be one of ' . implode(', ', array_keys($types))); - } - - $this->responseType = $responseType; - - return $this; - } - - public function getResponseNotes() - { - return $this->responseNotes; - } - - /** - * Set notes about the response of the operation - * - * @param string $notes Response notes - * - * @return self - */ - public function setResponseNotes($notes) - { - $this->responseNotes = $notes; - - return $this; - } - - public function getDeprecated() - { - return $this->deprecated; - } - - /** - * Set whether or not the command is deprecated - * - * @param bool $isDeprecated Set to true to mark as deprecated - * - * @return self - */ - public function setDeprecated($isDeprecated) - { - $this->deprecated = $isDeprecated; - - return $this; - } - - public function getUri() - { - return $this->uri; - } - - /** - * Set the URI template of the command - * - * @param string $uri URI template to set - * - * @return self - */ - public function setUri($uri) - { - $this->uri = $uri; - - return $this; - } - - public function getErrorResponses() - { - return $this->errorResponses; - } - - /** - * Add an error to the command - * - * @param string $code HTTP response code - * @param string $reason HTTP response reason phrase or information about the error - * @param string $class Exception class associated with the error - * - * @return self - */ - public function addErrorResponse($code, $reason, $class) - { - $this->errorResponses[] = array('code' => $code, 'reason' => $reason, 'class' => $class); - - return $this; - } - - /** - * Set all of the error responses of the operation - * - * @param array $errorResponses Hash of error name to a hash containing a code, reason, class - * - * @return self - */ - public function setErrorResponses(array $errorResponses) - { - $this->errorResponses = $errorResponses; - - return $this; - } - - public function getData($name) - { - return isset($this->data[$name]) ? $this->data[$name] : null; - } - - /** - * Set a particular data point on the operation - * - * @param string $name Name of the data value - * @param mixed $value Value to set - * - * @return self - */ - public function setData($name, $value) - { - $this->data[$name] = $value; - - return $this; - } - - /** - * Get the additionalParameters of the operation - * - * @return Parameter|null - */ - public function getAdditionalParameters() - { - return $this->additionalParameters; - } - - /** - * Set the additionalParameters of the operation - * - * @param Parameter|null $parameter Parameter to set - * - * @return self - */ - public function setAdditionalParameters($parameter) - { - if ($this->additionalParameters = $parameter) { - $this->additionalParameters->setParent($this); - } - - return $this; - } - - /** - * Infer the response type from the responseClass value - */ - protected function inferResponseType() - { - static $primitives = array('array' => 1, 'boolean' => 1, 'string' => 1, 'integer' => 1, '' => 1); - if (isset($primitives[$this->responseClass])) { - $this->responseType = self::TYPE_PRIMITIVE; - } elseif ($this->description && $this->description->hasModel($this->responseClass)) { - $this->responseType = self::TYPE_MODEL; - } else { - $this->responseType = self::TYPE_CLASS; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php deleted file mode 100644 index 4de41bd67..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/OperationInterface.php +++ /dev/null @@ -1,159 +0,0 @@ -getModel($data['$ref'])) { - $data = $model->toArray() + $data; - } - } elseif (isset($data['extends'])) { - // If this parameter extends from another parameter then start with the actual data - // union in the parent's data (e.g. actual supersedes parent) - if ($extends = $description->getModel($data['extends'])) { - $data += $extends->toArray(); - } - } - } - - // Pull configuration data into the parameter - foreach ($data as $key => $value) { - $this->{$key} = $value; - } - - $this->serviceDescription = $description; - $this->required = (bool) $this->required; - $this->data = (array) $this->data; - - if ($this->filters) { - $this->setFilters((array) $this->filters); - } - - if ($this->type == 'object' && $this->additionalProperties === null) { - $this->additionalProperties = true; - } - } - - /** - * Convert the object to an array - * - * @return array - */ - public function toArray() - { - static $checks = array('required', 'description', 'static', 'type', 'format', 'instanceOf', 'location', 'sentAs', - 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'data', 'enum', - 'filters'); - - $result = array(); - - // Anything that is in the `Items` attribute of an array *must* include it's name if available - if ($this->parent instanceof self && $this->parent->getType() == 'array' && isset($this->name)) { - $result['name'] = $this->name; - } - - foreach ($checks as $c) { - if ($value = $this->{$c}) { - $result[$c] = $value; - } - } - - if ($this->default !== null) { - $result['default'] = $this->default; - } - - if ($this->items !== null) { - $result['items'] = $this->getItems()->toArray(); - } - - if ($this->additionalProperties !== null) { - $result['additionalProperties'] = $this->getAdditionalProperties(); - if ($result['additionalProperties'] instanceof self) { - $result['additionalProperties'] = $result['additionalProperties']->toArray(); - } - } - - if ($this->type == 'object' && $this->properties) { - $result['properties'] = array(); - foreach ($this->getProperties() as $name => $property) { - $result['properties'][$name] = $property->toArray(); - } - } - - return $result; - } - - /** - * Get the default or static value of the command based on a value - * - * @param string $value Value that is currently set - * - * @return mixed Returns the value, a static value if one is present, or a default value - */ - public function getValue($value) - { - if ($this->static || ($this->default !== null && $value === null)) { - return $this->default; - } - - return $value; - } - - /** - * Run a value through the filters OR format attribute associated with the parameter - * - * @param mixed $value Value to filter - * - * @return mixed Returns the filtered value - */ - public function filter($value) - { - // Formats are applied exclusively and supersed filters - if ($this->format) { - return SchemaFormatter::format($this->format, $value); - } - - // Convert Boolean values - if ($this->type == 'boolean' && !is_bool($value)) { - $value = filter_var($value, FILTER_VALIDATE_BOOLEAN); - } - - // Apply filters to the value - if ($this->filters) { - foreach ($this->filters as $filter) { - if (is_array($filter)) { - // Convert complex filters that hold value place holders - foreach ($filter['args'] as &$data) { - if ($data == '@value') { - $data = $value; - } elseif ($data == '@api') { - $data = $this; - } - } - $value = call_user_func_array($filter['method'], $filter['args']); - } else { - $value = call_user_func($filter, $value); - } - } - } - - return $value; - } - - /** - * Get the name of the parameter - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Get the key of the parameter, where sentAs will supersede name if it is set - * - * @return string - */ - public function getWireName() - { - return $this->sentAs ?: $this->name; - } - - /** - * Set the name of the parameter - * - * @param string $name Name to set - * - * @return self - */ - public function setName($name) - { - $this->name = $name; - - return $this; - } - - /** - * Get the type(s) of the parameter - * - * @return string|array - */ - public function getType() - { - return $this->type; - } - - /** - * Set the type(s) of the parameter - * - * @param string|array $type Type of parameter or array of simple types used in a union - * - * @return self - */ - public function setType($type) - { - $this->type = $type; - - return $this; - } - - /** - * Get if the parameter is required - * - * @return bool - */ - public function getRequired() - { - return $this->required; - } - - /** - * Set if the parameter is required - * - * @param bool $isRequired Whether or not the parameter is required - * - * @return self - */ - public function setRequired($isRequired) - { - $this->required = (bool) $isRequired; - - return $this; - } - - /** - * Get the default value of the parameter - * - * @return string|null - */ - public function getDefault() - { - return $this->default; - } - - /** - * Set the default value of the parameter - * - * @param string|null $default Default value to set - * - * @return self - */ - public function setDefault($default) - { - $this->default = $default; - - return $this; - } - - /** - * Get the description of the parameter - * - * @return string|null - */ - public function getDescription() - { - return $this->description; - } - - /** - * Set the description of the parameter - * - * @param string $description Description - * - * @return self - */ - public function setDescription($description) - { - $this->description = $description; - - return $this; - } - - /** - * Get the minimum acceptable value for an integer - * - * @return int|null - */ - public function getMinimum() - { - return $this->minimum; - } - - /** - * Set the minimum acceptable value for an integer - * - * @param int|null $min Minimum - * - * @return self - */ - public function setMinimum($min) - { - $this->minimum = $min; - - return $this; - } - - /** - * Get the maximum acceptable value for an integer - * - * @return int|null - */ - public function getMaximum() - { - return $this->maximum; - } - - /** - * Set the maximum acceptable value for an integer - * - * @param int $max Maximum - * - * @return self - */ - public function setMaximum($max) - { - $this->maximum = $max; - - return $this; - } - - /** - * Get the minimum allowed length of a string value - * - * @return int - */ - public function getMinLength() - { - return $this->minLength; - } - - /** - * Set the minimum allowed length of a string value - * - * @param int|null $min Minimum - * - * @return self - */ - public function setMinLength($min) - { - $this->minLength = $min; - - return $this; - } - - /** - * Get the maximum allowed length of a string value - * - * @return int|null - */ - public function getMaxLength() - { - return $this->maxLength; - } - - /** - * Set the maximum allowed length of a string value - * - * @param int $max Maximum length - * - * @return self - */ - public function setMaxLength($max) - { - $this->maxLength = $max; - - return $this; - } - - /** - * Get the maximum allowed number of items in an array value - * - * @return int|null - */ - public function getMaxItems() - { - return $this->maxItems; - } - - /** - * Set the maximum allowed number of items in an array value - * - * @param int $max Maximum - * - * @return self - */ - public function setMaxItems($max) - { - $this->maxItems = $max; - - return $this; - } - - /** - * Get the minimum allowed number of items in an array value - * - * @return int - */ - public function getMinItems() - { - return $this->minItems; - } - - /** - * Set the minimum allowed number of items in an array value - * - * @param int|null $min Minimum - * - * @return self - */ - public function setMinItems($min) - { - $this->minItems = $min; - - return $this; - } - - /** - * Get the location of the parameter - * - * @return string|null - */ - public function getLocation() - { - return $this->location; - } - - /** - * Set the location of the parameter - * - * @param string|null $location Location of the parameter - * - * @return self - */ - public function setLocation($location) - { - $this->location = $location; - - return $this; - } - - /** - * Get the sentAs attribute of the parameter that used with locations to sentAs an attribute when it is being - * applied to a location. - * - * @return string|null - */ - public function getSentAs() - { - return $this->sentAs; - } - - /** - * Set the sentAs attribute - * - * @param string|null $name Name of the value as it is sent over the wire - * - * @return self - */ - public function setSentAs($name) - { - $this->sentAs = $name; - - return $this; - } - - /** - * Retrieve a known property from the parameter by name or a data property by name. When not specific name value - * is specified, all data properties will be returned. - * - * @param string|null $name Specify a particular property name to retrieve - * - * @return array|mixed|null - */ - public function getData($name = null) - { - if (!$name) { - return $this->data; - } - - if (isset($this->data[$name])) { - return $this->data[$name]; - } elseif (isset($this->{$name})) { - return $this->{$name}; - } - - return null; - } - - /** - * Set the extra data properties of the parameter or set a specific extra property - * - * @param string|array|null $nameOrData The name of a specific extra to set or an array of extras to set - * @param mixed|null $data When setting a specific extra property, specify the data to set for it - * - * @return self - */ - public function setData($nameOrData, $data = null) - { - if (is_array($nameOrData)) { - $this->data = $nameOrData; - } else { - $this->data[$nameOrData] = $data; - } - - return $this; - } - - /** - * Get whether or not the default value can be changed - * - * @return mixed|null - */ - public function getStatic() - { - return $this->static; - } - - /** - * Set to true if the default value cannot be changed - * - * @param bool $static True or false - * - * @return self - */ - public function setStatic($static) - { - $this->static = (bool) $static; - - return $this; - } - - /** - * Get an array of filters used by the parameter - * - * @return array - */ - public function getFilters() - { - return $this->filters ?: array(); - } - - /** - * Set the array of filters used by the parameter - * - * @param array $filters Array of functions to use as filters - * - * @return self - */ - public function setFilters(array $filters) - { - $this->filters = array(); - foreach ($filters as $filter) { - $this->addFilter($filter); - } - - return $this; - } - - /** - * Add a filter to the parameter - * - * @param string|array $filter Method to filter the value through - * - * @return self - * @throws InvalidArgumentException - */ - public function addFilter($filter) - { - if (is_array($filter)) { - if (!isset($filter['method'])) { - throw new InvalidArgumentException('A [method] value must be specified for each complex filter'); - } - } - - if (!$this->filters) { - $this->filters = array($filter); - } else { - $this->filters[] = $filter; - } - - return $this; - } - - /** - * Get the parent object (an {@see OperationInterface} or {@see Parameter} - * - * @return OperationInterface|Parameter|null - */ - public function getParent() - { - return $this->parent; - } - - /** - * Set the parent object of the parameter - * - * @param OperationInterface|Parameter|null $parent Parent container of the parameter - * - * @return self - */ - public function setParent($parent) - { - $this->parent = $parent; - - return $this; - } - - /** - * Get the properties of the parameter - * - * @return array - */ - public function getProperties() - { - if (!$this->propertiesCache) { - $this->propertiesCache = array(); - foreach (array_keys($this->properties) as $name) { - $this->propertiesCache[$name] = $this->getProperty($name); - } - } - - return $this->propertiesCache; - } - - /** - * Get a specific property from the parameter - * - * @param string $name Name of the property to retrieve - * - * @return null|Parameter - */ - public function getProperty($name) - { - if (!isset($this->properties[$name])) { - return null; - } - - if (!($this->properties[$name] instanceof self)) { - $this->properties[$name]['name'] = $name; - $this->properties[$name] = new static($this->properties[$name], $this->serviceDescription); - $this->properties[$name]->setParent($this); - } - - return $this->properties[$name]; - } - - /** - * Remove a property from the parameter - * - * @param string $name Name of the property to remove - * - * @return self - */ - public function removeProperty($name) - { - unset($this->properties[$name]); - $this->propertiesCache = null; - - return $this; - } - - /** - * Add a property to the parameter - * - * @param Parameter $property Properties to set - * - * @return self - */ - public function addProperty(Parameter $property) - { - $this->properties[$property->getName()] = $property; - $property->setParent($this); - $this->propertiesCache = null; - - return $this; - } - - /** - * Get the additionalProperties value of the parameter - * - * @return bool|Parameter|null - */ - public function getAdditionalProperties() - { - if (is_array($this->additionalProperties)) { - $this->additionalProperties = new static($this->additionalProperties, $this->serviceDescription); - $this->additionalProperties->setParent($this); - } - - return $this->additionalProperties; - } - - /** - * Set the additionalProperties value of the parameter - * - * @param bool|Parameter|null $additional Boolean to allow any, an Parameter to specify a schema, or false to disallow - * - * @return self - */ - public function setAdditionalProperties($additional) - { - $this->additionalProperties = $additional; - - return $this; - } - - /** - * Set the items data of the parameter - * - * @param Parameter|null $items Items to set - * - * @return self - */ - public function setItems(Parameter $items = null) - { - if ($this->items = $items) { - $this->items->setParent($this); - } - - return $this; - } - - /** - * Get the item data of the parameter - * - * @return Parameter|null - */ - public function getItems() - { - if (is_array($this->items)) { - $this->items = new static($this->items, $this->serviceDescription); - $this->items->setParent($this); - } - - return $this->items; - } - - /** - * Get the class that the parameter must implement - * - * @return null|string - */ - public function getInstanceOf() - { - return $this->instanceOf; - } - - /** - * Set the class that the parameter must be an instance of - * - * @param string|null $instanceOf Class or interface name - * - * @return self - */ - public function setInstanceOf($instanceOf) - { - $this->instanceOf = $instanceOf; - - return $this; - } - - /** - * Get the enum of strings that are valid for the parameter - * - * @return array|null - */ - public function getEnum() - { - return $this->enum; - } - - /** - * Set the enum of strings that are valid for the parameter - * - * @param array|null $enum Array of strings or null - * - * @return self - */ - public function setEnum(array $enum = null) - { - $this->enum = $enum; - - return $this; - } - - /** - * Get the regex pattern that must match a value when the value is a string - * - * @return string - */ - public function getPattern() - { - return $this->pattern; - } - - /** - * Set the regex pattern that must match a value when the value is a string - * - * @param string $pattern Regex pattern - * - * @return self - */ - public function setPattern($pattern) - { - $this->pattern = $pattern; - - return $this; - } - - /** - * Get the format attribute of the schema - * - * @return string - */ - public function getFormat() - { - return $this->format; - } - - /** - * Set the format attribute of the schema - * - * @param string $format Format to set (e.g. date, date-time, timestamp, time, date-time-http) - * - * @return self - */ - public function setFormat($format) - { - $this->format = $format; - - return $this; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php deleted file mode 100644 index 7f47fc9d7..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaFormatter.php +++ /dev/null @@ -1,156 +0,0 @@ -setTimezone(self::getUtcTimeZone())->format($format); - } - - throw new InvalidArgumentException('Date/Time values must be either a string, integer, or DateTime object'); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php deleted file mode 100644 index b045422d4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/SchemaValidator.php +++ /dev/null @@ -1,291 +0,0 @@ -castIntegerToStringType = $castIntegerToStringType; - } - - public function validate(Parameter $param, &$value) - { - $this->errors = array(); - $this->recursiveProcess($param, $value); - - if (empty($this->errors)) { - return true; - } else { - sort($this->errors); - return false; - } - } - - /** - * Get the errors encountered while validating - * - * @return array - */ - public function getErrors() - { - return $this->errors ?: array(); - } - - /** - * Recursively validate a parameter - * - * @param Parameter $param API parameter being validated - * @param mixed $value Value to validate and validate. The value may change during this validate. - * @param string $path Current validation path (used for error reporting) - * @param int $depth Current depth in the validation validate - * - * @return bool Returns true if valid, or false if invalid - */ - protected function recursiveProcess(Parameter $param, &$value, $path = '', $depth = 0) - { - // Update the value by adding default or static values - $value = $param->getValue($value); - - $required = $param->getRequired(); - // if the value is null and the parameter is not required or is static, then skip any further recursion - if ((null === $value && !$required) || $param->getStatic()) { - return true; - } - - $type = $param->getType(); - // Attempt to limit the number of times is_array is called by tracking if the value is an array - $valueIsArray = is_array($value); - // If a name is set then update the path so that validation messages are more helpful - if ($name = $param->getName()) { - $path .= "[{$name}]"; - } - - if ($type == 'object') { - - // Objects are either associative arrays, ToArrayInterface, or some other object - if ($param->getInstanceOf()) { - $instance = $param->getInstanceOf(); - if (!($value instanceof $instance)) { - $this->errors[] = "{$path} must be an instance of {$instance}"; - return false; - } - } - - // Determine whether or not this "value" has properties and should be traversed - $traverse = $temporaryValue = false; - - // Convert the value to an array - if (!$valueIsArray && $value instanceof ToArrayInterface) { - $value = $value->toArray(); - } - - if ($valueIsArray) { - // Ensure that the array is associative and not numerically indexed - if (isset($value[0])) { - $this->errors[] = "{$path} must be an array of properties. Got a numerically indexed array."; - return false; - } - $traverse = true; - } elseif ($value === null) { - // Attempt to let the contents be built up by default values if possible - $value = array(); - $temporaryValue = $valueIsArray = $traverse = true; - } - - if ($traverse) { - - if ($properties = $param->getProperties()) { - // if properties were found, the validate each property of the value - foreach ($properties as $property) { - $name = $property->getName(); - if (isset($value[$name])) { - $this->recursiveProcess($property, $value[$name], $path, $depth + 1); - } else { - $current = null; - $this->recursiveProcess($property, $current, $path, $depth + 1); - // Only set the value if it was populated with something - if (null !== $current) { - $value[$name] = $current; - } - } - } - } - - $additional = $param->getAdditionalProperties(); - if ($additional !== true) { - // If additional properties were found, then validate each against the additionalProperties attr. - $keys = array_keys($value); - // Determine the keys that were specified that were not listed in the properties of the schema - $diff = array_diff($keys, array_keys($properties)); - if (!empty($diff)) { - // Determine which keys are not in the properties - if ($additional instanceOf Parameter) { - foreach ($diff as $key) { - $this->recursiveProcess($additional, $value[$key], "{$path}[{$key}]", $depth); - } - } else { - // if additionalProperties is set to false and there are additionalProperties in the values, then fail - foreach ($diff as $prop) { - $this->errors[] = sprintf('%s[%s] is not an allowed property', $path, $prop); - } - } - } - } - - // A temporary value will be used to traverse elements that have no corresponding input value. - // This allows nested required parameters with default values to bubble up into the input. - // Here we check if we used a temp value and nothing bubbled up, then we need to remote the value. - if ($temporaryValue && empty($value)) { - $value = null; - $valueIsArray = false; - } - } - - } elseif ($type == 'array' && $valueIsArray && $param->getItems()) { - foreach ($value as $i => &$item) { - // Validate each item in an array against the items attribute of the schema - $this->recursiveProcess($param->getItems(), $item, $path . "[{$i}]", $depth + 1); - } - } - - // If the value is required and the type is not null, then there is an error if the value is not set - if ($required && $value === null && $type != 'null') { - $message = "{$path} is " . ($param->getType() ? ('a required ' . implode(' or ', (array) $param->getType())) : 'required'); - if ($param->getDescription()) { - $message .= ': ' . $param->getDescription(); - } - $this->errors[] = $message; - return false; - } - - // Validate that the type is correct. If the type is string but an integer was passed, the class can be - // instructed to cast the integer to a string to pass validation. This is the default behavior. - if ($type && (!$type = $this->determineType($type, $value))) { - if ($this->castIntegerToStringType && $param->getType() == 'string' && is_integer($value)) { - $value = (string) $value; - } else { - $this->errors[] = "{$path} must be of type " . implode(' or ', (array) $param->getType()); - } - } - - // Perform type specific validation for strings, arrays, and integers - if ($type == 'string') { - - // Strings can have enums which are a list of predefined values - if (($enum = $param->getEnum()) && !in_array($value, $enum)) { - $this->errors[] = "{$path} must be one of " . implode(' or ', array_map(function ($s) { - return '"' . addslashes($s) . '"'; - }, $enum)); - } - // Strings can have a regex pattern that the value must match - if (($pattern = $param->getPattern()) && !preg_match($pattern, $value)) { - $this->errors[] = "{$path} must match the following regular expression: {$pattern}"; - } - - $strLen = null; - if ($min = $param->getMinLength()) { - $strLen = strlen($value); - if ($strLen < $min) { - $this->errors[] = "{$path} length must be greater than or equal to {$min}"; - } - } - if ($max = $param->getMaxLength()) { - if (($strLen ?: strlen($value)) > $max) { - $this->errors[] = "{$path} length must be less than or equal to {$max}"; - } - } - - } elseif ($type == 'array') { - - $size = null; - if ($min = $param->getMinItems()) { - $size = count($value); - if ($size < $min) { - $this->errors[] = "{$path} must contain {$min} or more elements"; - } - } - if ($max = $param->getMaxItems()) { - if (($size ?: count($value)) > $max) { - $this->errors[] = "{$path} must contain {$max} or fewer elements"; - } - } - - } elseif ($type == 'integer' || $type == 'number' || $type == 'numeric') { - if (($min = $param->getMinimum()) && $value < $min) { - $this->errors[] = "{$path} must be greater than or equal to {$min}"; - } - if (($max = $param->getMaximum()) && $value > $max) { - $this->errors[] = "{$path} must be less than or equal to {$max}"; - } - } - - return empty($this->errors); - } - - /** - * From the allowable types, determine the type that the variable matches - * - * @param string $type Parameter type - * @param mixed $value Value to determine the type - * - * @return string|bool Returns the matching type on - */ - protected function determineType($type, $value) - { - foreach ((array) $type as $t) { - if ($t == 'string' && (is_string($value) || (is_object($value) && method_exists($value, '__toString')))) { - return 'string'; - } elseif ($t == 'object' && (is_array($value) || is_object($value))) { - return 'object'; - } elseif ($t == 'array' && is_array($value)) { - return 'array'; - } elseif ($t == 'integer' && is_integer($value)) { - return 'integer'; - } elseif ($t == 'boolean' && is_bool($value)) { - return 'boolean'; - } elseif ($t == 'number' && is_numeric($value)) { - return 'number'; - } elseif ($t == 'numeric' && is_numeric($value)) { - return 'numeric'; - } elseif ($t == 'null' && !$value) { - return 'null'; - } elseif ($t == 'any') { - return 'any'; - } - } - - return false; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php deleted file mode 100644 index 286e65eec..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php +++ /dev/null @@ -1,271 +0,0 @@ -load($config, $options); - } - - /** - * @param array $config Array of configuration data - */ - public function __construct(array $config = array()) - { - $this->fromArray($config); - } - - public function serialize() - { - return json_encode($this->toArray()); - } - - public function unserialize($json) - { - $this->operations = array(); - $this->fromArray(json_decode($json, true)); - } - - public function toArray() - { - $result = array( - 'name' => $this->name, - 'apiVersion' => $this->apiVersion, - 'baseUrl' => $this->baseUrl, - 'description' => $this->description - ) + $this->extraData; - $result['operations'] = array(); - foreach ($this->getOperations() as $name => $operation) { - $result['operations'][$operation->getName() ?: $name] = $operation->toArray(); - } - if (!empty($this->models)) { - $result['models'] = array(); - foreach ($this->models as $id => $model) { - $result['models'][$id] = $model instanceof Parameter ? $model->toArray(): $model; - } - } - - return array_filter($result); - } - - public function getBaseUrl() - { - return $this->baseUrl; - } - - /** - * Set the baseUrl of the description - * - * @param string $baseUrl Base URL of each operation - * - * @return self - */ - public function setBaseUrl($baseUrl) - { - $this->baseUrl = $baseUrl; - - return $this; - } - - public function getOperations() - { - foreach (array_keys($this->operations) as $name) { - $this->getOperation($name); - } - - return $this->operations; - } - - public function hasOperation($name) - { - return isset($this->operations[$name]); - } - - public function getOperation($name) - { - // Lazily retrieve and build operations - if (!isset($this->operations[$name])) { - return null; - } - - if (!($this->operations[$name] instanceof Operation)) { - $this->operations[$name] = new Operation($this->operations[$name], $this); - } - - return $this->operations[$name]; - } - - /** - * Add a operation to the service description - * - * @param OperationInterface $operation Operation to add - * - * @return self - */ - public function addOperation(OperationInterface $operation) - { - $this->operations[$operation->getName()] = $operation->setServiceDescription($this); - - return $this; - } - - public function getModel($id) - { - if (!isset($this->models[$id])) { - return null; - } - - if (!($this->models[$id] instanceof Parameter)) { - $this->models[$id] = new Parameter($this->models[$id] + array('name' => $id), $this); - } - - return $this->models[$id]; - } - - public function getModels() - { - // Ensure all models are converted into parameter objects - foreach (array_keys($this->models) as $id) { - $this->getModel($id); - } - - return $this->models; - } - - public function hasModel($id) - { - return isset($this->models[$id]); - } - - /** - * Add a model to the service description - * - * @param Parameter $model Model to add - * - * @return self - */ - public function addModel(Parameter $model) - { - $this->models[$model->getName()] = $model; - - return $this; - } - - public function getApiVersion() - { - return $this->apiVersion; - } - - public function getName() - { - return $this->name; - } - - public function getDescription() - { - return $this->description; - } - - public function getData($key) - { - return isset($this->extraData[$key]) ? $this->extraData[$key] : null; - } - - public function setData($key, $value) - { - $this->extraData[$key] = $value; - - return $this; - } - - /** - * Initialize the state from an array - * - * @param array $config Configuration data - * @throws InvalidArgumentException - */ - protected function fromArray(array $config) - { - // Keep a list of default keys used in service descriptions that is later used to determine extra data keys - static $defaultKeys = array('name', 'models', 'apiVersion', 'baseUrl', 'description'); - // Pull in the default configuration values - foreach ($defaultKeys as $key) { - if (isset($config[$key])) { - $this->{$key} = $config[$key]; - } - } - - // Account for the Swagger name for Guzzle's baseUrl - if (isset($config['basePath'])) { - $this->baseUrl = $config['basePath']; - } - - // Ensure that the models and operations properties are always arrays - $this->models = (array) $this->models; - $this->operations = (array) $this->operations; - - // We want to add operations differently than adding the other properties - $defaultKeys[] = 'operations'; - - // Create operations for each operation - if (isset($config['operations'])) { - foreach ($config['operations'] as $name => $operation) { - if (!($operation instanceof Operation) && !is_array($operation)) { - throw new InvalidArgumentException('Invalid operation in service description: ' - . gettype($operation)); - } - $this->operations[$name] = $operation; - } - } - - // Get all of the additional properties of the service description and store them in a data array - foreach (array_diff(array_keys($config), $defaultKeys) as $key) { - $this->extraData[$key] = $config[$key]; - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php deleted file mode 100644 index 5983e586b..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionInterface.php +++ /dev/null @@ -1,106 +0,0 @@ - $op) { - $name = $op['name'] = isset($op['name']) ? $op['name'] : $name; - // Extend other operations - if (!empty($op['extends'])) { - $this->resolveExtension($name, $op, $operations); - } - $op['parameters'] = isset($op['parameters']) ? $op['parameters'] : array(); - $operations[$name] = $op; - } - } - - return new ServiceDescription(array( - 'apiVersion' => isset($config['apiVersion']) ? $config['apiVersion'] : null, - 'baseUrl' => isset($config['baseUrl']) ? $config['baseUrl'] : null, - 'description' => isset($config['description']) ? $config['description'] : null, - 'operations' => $operations, - 'models' => isset($config['models']) ? $config['models'] : null - ) + $config); - } - - /** - * @param string $name Name of the operation - * @param array $op Operation value array - * @param array $operations Currently loaded operations - * @throws DescriptionBuilderException when extending a non-existent operation - */ - protected function resolveExtension($name, array &$op, array &$operations) - { - $resolved = array(); - $original = empty($op['parameters']) ? false: $op['parameters']; - $hasClass = !empty($op['class']); - foreach ((array) $op['extends'] as $extendedCommand) { - if (empty($operations[$extendedCommand])) { - throw new DescriptionBuilderException("{$name} extends missing operation {$extendedCommand}"); - } - $toArray = $operations[$extendedCommand]; - $resolved = empty($resolved) - ? $toArray['parameters'] - : array_merge($resolved, $toArray['parameters']); - - $op = $op + $toArray; - if (!$hasClass && isset($toArray['class'])) { - $op['class'] = $toArray['class']; - } - } - $op['parameters'] = $original ? array_merge($resolved, $original) : $resolved; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php deleted file mode 100644 index 94ca77da4..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Description/ValidatorInterface.php +++ /dev/null @@ -1,28 +0,0 @@ -getMessage(), $e->getCode(), $e->getPrevious()); - $ce->setSuccessfulRequests($e->getSuccessfulRequests()); - - $alreadyAddedExceptions = array(); - foreach ($e->getFailedRequests() as $request) { - if ($re = $e->getExceptionForFailedRequest($request)) { - $alreadyAddedExceptions[] = $re; - $ce->addFailedRequestWithException($request, $re); - } else { - $ce->addFailedRequest($request); - } - } - - // Add any exceptions that did not map to a request - if (count($alreadyAddedExceptions) < count($e)) { - foreach ($e as $ex) { - if (!in_array($ex, $alreadyAddedExceptions)) { - $ce->add($ex); - } - } - } - - return $ce; - } - - /** - * Get all of the commands in the transfer - * - * @return array - */ - public function getAllCommands() - { - return array_merge($this->successfulCommands, $this->failedCommands); - } - - /** - * Add to the array of successful commands - * - * @param CommandInterface $command Successful command - * - * @return self - */ - public function addSuccessfulCommand(CommandInterface $command) - { - $this->successfulCommands[] = $command; - - return $this; - } - - /** - * Add to the array of failed commands - * - * @param CommandInterface $command Failed command - * - * @return self - */ - public function addFailedCommand(CommandInterface $command) - { - $this->failedCommands[] = $command; - - return $this; - } - - /** - * Get an array of successful commands - * - * @return array - */ - public function getSuccessfulCommands() - { - return $this->successfulCommands; - } - - /** - * Get an array of failed commands - * - * @return array - */ - public function getFailedCommands() - { - return $this->failedCommands; - } - - /** - * Get the Exception that caused the given $command to fail - * - * @param CommandInterface $command Failed command - * - * @return \Exception|null - */ - public function getExceptionForFailedCommand(CommandInterface $command) - { - return $this->getExceptionForFailedRequest($command->getRequest()); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php deleted file mode 100644 index 1407e5687..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/DescriptionBuilderException.php +++ /dev/null @@ -1,7 +0,0 @@ -invalidCommands = $commands; - parent::__construct( - 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . - 'strategy you use with a command transfer must divide command batches by client.' - ); - } - - /** - * Get the invalid commands - * - * @return array - */ - public function getCommands() - { - return $this->invalidCommands; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php deleted file mode 100644 index d59ff2185..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Exception/ResponseClassException.php +++ /dev/null @@ -1,9 +0,0 @@ -errors = $errors; - } - - /** - * Get any validation errors - * - * @return array - */ - public function getErrors() - { - return $this->errors; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php deleted file mode 100644 index 21140e772..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/AbstractResourceIteratorFactory.php +++ /dev/null @@ -1,37 +0,0 @@ -canBuild($command)) { - throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); - } - - $className = $this->getClassName($command); - - return new $className($command, $options); - } - - public function canBuild(CommandInterface $command) - { - return (bool) $this->getClassName($command); - } - - /** - * Get the name of the class to instantiate for the command - * - * @param CommandInterface $command Command that is associated with the iterator - * - * @return string - */ - abstract protected function getClassName(CommandInterface $command); -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php deleted file mode 100644 index 2efc133c6..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/CompositeResourceIteratorFactory.php +++ /dev/null @@ -1,67 +0,0 @@ -factories = $factories; - } - - public function build(CommandInterface $command, array $options = array()) - { - if (!($factory = $this->getFactory($command))) { - throw new InvalidArgumentException('Iterator was not found for ' . $command->getName()); - } - - return $factory->build($command, $options); - } - - public function canBuild(CommandInterface $command) - { - return $this->getFactory($command) !== false; - } - - /** - * Add a factory to the composite factory - * - * @param ResourceIteratorFactoryInterface $factory Factory to add - * - * @return self - */ - public function addFactory(ResourceIteratorFactoryInterface $factory) - { - $this->factories[] = $factory; - - return $this; - } - - /** - * Get the factory that matches the command object - * - * @param CommandInterface $command Command retrieving the iterator for - * - * @return ResourceIteratorFactoryInterface|bool - */ - protected function getFactory(CommandInterface $command) - { - foreach ($this->factories as $factory) { - if ($factory->canBuild($command)) { - return $factory; - } - } - - return false; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php deleted file mode 100644 index c71ca9d85..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/MapResourceIteratorFactory.php +++ /dev/null @@ -1,34 +0,0 @@ -map = $map; - } - - public function getClassName(CommandInterface $command) - { - $className = $command->getName(); - - if (isset($this->map[$className])) { - return $this->map[$className]; - } elseif (isset($this->map['*'])) { - // If a wildcard was added, then always use that - return $this->map['*']; - } - - return null; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php deleted file mode 100644 index 2322434a5..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/Model.php +++ /dev/null @@ -1,64 +0,0 @@ -data = $data; - $this->structure = $structure; - } - - /** - * Get the structure of the model - * - * @return Parameter - */ - public function getStructure() - { - return $this->structure ?: new Parameter(); - } - - /** - * Provides debug information about the model object - * - * @return string - */ - public function __toString() - { - $output = 'Debug output of '; - if ($this->structure) { - $output .= $this->structure->getName() . ' '; - } - $output .= 'model'; - $output = str_repeat('=', strlen($output)) . "\n" . $output . "\n" . str_repeat('=', strlen($output)) . "\n\n"; - $output .= "Model data\n-----------\n\n"; - $output .= "This data can be retrieved from the model object using the get() method of the model " - . "(e.g. \$model->get(\$key)) or accessing the model like an associative array (e.g. \$model['key']).\n\n"; - $lines = array_slice(explode("\n", trim(print_r($this->toArray(), true))), 2, -1); - $output .= implode("\n", $lines); - - if ($this->structure) { - $output .= "\n\nModel structure\n---------------\n\n"; - $output .= "The following JSON document defines how the model was parsed from an HTTP response into the " - . "associative array structure you see above.\n\n"; - $output .= ' ' . json_encode($this->structure->toArray()) . "\n\n"; - } - - return $output . "\n"; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php deleted file mode 100644 index e14152432..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php +++ /dev/null @@ -1,254 +0,0 @@ -originalCommand = $command; - - // Parse options from the array of options - $this->data = $data; - $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; - $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; - } - - /** - * Get all of the resources as an array (Warning: this could issue a large number of requests) - * - * @return array - */ - public function toArray() - { - return iterator_to_array($this, false); - } - - public function setLimit($limit) - { - $this->limit = $limit; - $this->resetState(); - - return $this; - } - - public function setPageSize($pageSize) - { - $this->pageSize = $pageSize; - $this->resetState(); - - return $this; - } - - /** - * Get an option from the iterator - * - * @param string $key Key of the option to retrieve - * - * @return mixed|null Returns NULL if not set or the value if set - */ - public function get($key) - { - return array_key_exists($key, $this->data) ? $this->data[$key] : null; - } - - /** - * Set an option on the iterator - * - * @param string $key Key of the option to set - * @param mixed $value Value to set for the option - * - * @return ResourceIterator - */ - public function set($key, $value) - { - $this->data[$key] = $value; - - return $this; - } - - public function current() - { - return $this->resources ? current($this->resources) : false; - } - - public function key() - { - return max(0, $this->iteratedCount - 1); - } - - public function count() - { - return $this->retrievedCount; - } - - /** - * Get the total number of requests sent - * - * @return int - */ - public function getRequestCount() - { - return $this->requestCount; - } - - /** - * Rewind the Iterator to the first element and send the original command - */ - public function rewind() - { - // Use the original command - $this->command = clone $this->originalCommand; - $this->resetState(); - $this->next(); - } - - public function valid() - { - return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) - && (!$this->limit || $this->iteratedCount < $this->limit + 1); - } - - public function next() - { - $this->iteratedCount++; - - // Check if a new set of resources needs to be retrieved - $sendRequest = false; - if (!$this->resources) { - $sendRequest = true; - } else { - // iterate over the internal array - $current = next($this->resources); - $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); - } - - if ($sendRequest) { - - $this->dispatch('resource_iterator.before_send', array( - 'iterator' => $this, - 'resources' => $this->resources - )); - - // Get a new command object from the original command - $this->command = clone $this->originalCommand; - // Send a request and retrieve the newly loaded resources - $this->resources = $this->sendRequest(); - $this->requestCount++; - - // If no resources were found, then the last request was not needed - // and iteration must stop - if (empty($this->resources)) { - $this->invalid = true; - } else { - // Add to the number of retrieved resources - $this->retrievedCount += count($this->resources); - // Ensure that we rewind to the beginning of the array - reset($this->resources); - } - - $this->dispatch('resource_iterator.after_send', array( - 'iterator' => $this, - 'resources' => $this->resources - )); - } - } - - /** - * Retrieve the NextToken that can be used in other iterators. - * - * @return string Returns a NextToken - */ - public function getNextToken() - { - return $this->nextToken; - } - - /** - * Returns the value that should be specified for the page size for a request that will maintain any hard limits, - * but still honor the specified pageSize if the number of items retrieved + pageSize < hard limit - * - * @return int Returns the page size of the next request. - */ - protected function calculatePageSize() - { - if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { - return 1 + ($this->limit - $this->iteratedCount); - } - - return (int) $this->pageSize; - } - - /** - * Reset the internal state of the iterator without triggering a rewind() - */ - protected function resetState() - { - $this->iteratedCount = 0; - $this->retrievedCount = 0; - $this->nextToken = false; - $this->resources = null; - $this->invalid = false; - } - - /** - * Send a request to retrieve the next page of results. Hook for subclasses to implement. - * - * @return array Returns the newly loaded resources - */ - abstract protected function sendRequest(); -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php deleted file mode 100644 index 6aa36153f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php +++ /dev/null @@ -1,111 +0,0 @@ -iterator = $iterator; - $this->callback = $callback; - Version::warn(__CLASS__ . ' is deprecated'); - } - - /** - * Apply the callback to the contents of the resource iterator - * - * @param int $perBatch The number of records to group per batch transfer - * - * @return int Returns the number of iterated resources - */ - public function apply($perBatch = 50) - { - $this->iterated = $this->batches = $batches = 0; - $that = $this; - $it = $this->iterator; - $callback = $this->callback; - - $batch = BatchBuilder::factory() - ->createBatchesWith(new BatchSizeDivisor($perBatch)) - ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { - $batches++; - $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); - call_user_func_array($callback, array($it, $batch)); - $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); - })) - ->autoFlushAt($perBatch) - ->build(); - - $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); - - foreach ($this->iterator as $resource) { - $this->iterated++; - $batch->add($resource); - } - - $batch->flush(); - $this->batches = $batches; - - return $this->iterated; - } - - /** - * Get the total number of batches sent - * - * @return int - */ - public function getBatchCount() - { - return $this->batches; - } - - /** - * Get the total number of iterated resources - * - * @return int - */ - public function getIteratedCount() - { - return $this->iterated; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php deleted file mode 100644 index 2fd998071..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php +++ /dev/null @@ -1,60 +0,0 @@ - AbcFoo). - */ -class ResourceIteratorClassFactory extends AbstractResourceIteratorFactory -{ - /** @var array List of namespaces used to look for classes */ - protected $namespaces; - - /** @var InflectorInterface Inflector used to determine class names */ - protected $inflector; - - /** - * @param string|array $namespaces List of namespaces for iterator objects - * @param InflectorInterface $inflector Inflector used to resolve class names - */ - public function __construct($namespaces = array(), InflectorInterface $inflector = null) - { - $this->namespaces = (array) $namespaces; - $this->inflector = $inflector ?: Inflector::getDefault(); - } - - /** - * Registers a namespace to check for Iterators - * - * @param string $namespace Namespace which contains Iterator classes - * - * @return self - */ - public function registerNamespace($namespace) - { - array_unshift($this->namespaces, $namespace); - - return $this; - } - - protected function getClassName(CommandInterface $command) - { - $iteratorName = $this->inflector->camel($command->getName()) . 'Iterator'; - - // Determine the name of the class to load - foreach ($this->namespaces as $namespace) { - $potentialClassName = $namespace . '\\' . $iteratorName; - if (class_exists($potentialClassName)) { - return $potentialClassName; - } - } - - return false; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php deleted file mode 100644 index 8b4e8dbe0..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -=5.3.2", - "guzzle/cache": "self.version", - "guzzle/http": "self.version", - "guzzle/inflection": "self.version" - }, - "autoload": { - "psr-0": { "Guzzle\\Service": "" } - }, - "target-dir": "Guzzle/Service", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php deleted file mode 100644 index 9949e456f..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Stream/PhpStreamRequestFactory.php +++ /dev/null @@ -1,276 +0,0 @@ -contextOptions = stream_context_get_options($context); - $this->context = $context; - } elseif (is_array($context) || !$context) { - $this->contextOptions = $context; - $this->createContext($params); - } elseif ($context) { - throw new InvalidArgumentException('$context must be an array or resource'); - } - - // Dispatch the before send event - $request->dispatch('request.before_send', array( - 'request' => $request, - 'context' => $this->context, - 'context_options' => $this->contextOptions - )); - - $this->setUrl($request); - $this->addDefaultContextOptions($request); - $this->addSslOptions($request); - $this->addBodyOptions($request); - $this->addProxyOptions($request); - - // Create the file handle but silence errors - return $this->createStream($params) - ->setCustomData('request', $request) - ->setCustomData('response_headers', $this->getLastResponseHeaders()); - } - - /** - * Set an option on the context and the internal options array - * - * @param string $wrapper Stream wrapper name of http - * @param string $name Context name - * @param mixed $value Context value - * @param bool $overwrite Set to true to overwrite an existing value - */ - protected function setContextValue($wrapper, $name, $value, $overwrite = false) - { - if (!isset($this->contextOptions[$wrapper])) { - $this->contextOptions[$wrapper] = array($name => $value); - } elseif (!$overwrite && isset($this->contextOptions[$wrapper][$name])) { - return; - } - $this->contextOptions[$wrapper][$name] = $value; - stream_context_set_option($this->context, $wrapper, $name, $value); - } - - /** - * Create a stream context - * - * @param array $params Parameter array - */ - protected function createContext(array $params) - { - $options = $this->contextOptions; - $this->context = $this->createResource(function () use ($params, $options) { - return stream_context_create($options, $params); - }); - } - - /** - * Get the last response headers received by the HTTP request - * - * @return array - */ - public function getLastResponseHeaders() - { - return $this->lastResponseHeaders; - } - - /** - * Adds the default context options to the stream context options - * - * @param RequestInterface $request Request - */ - protected function addDefaultContextOptions(RequestInterface $request) - { - $this->setContextValue('http', 'method', $request->getMethod()); - $headers = $request->getHeaderLines(); - - // "Connection: close" is required to get streams to work in HTTP 1.1 - if (!$request->hasHeader('Connection')) { - $headers[] = 'Connection: close'; - } - - $this->setContextValue('http', 'header', $headers); - $this->setContextValue('http', 'protocol_version', $request->getProtocolVersion()); - $this->setContextValue('http', 'ignore_errors', true); - } - - /** - * Set the URL to use with the factory - * - * @param RequestInterface $request Request that owns the URL - */ - protected function setUrl(RequestInterface $request) - { - $this->url = $request->getUrl(true); - - // Check for basic Auth username - if ($request->getUsername()) { - $this->url->setUsername($request->getUsername()); - } - - // Check for basic Auth password - if ($request->getPassword()) { - $this->url->setPassword($request->getPassword()); - } - } - - /** - * Add SSL options to the stream context - * - * @param RequestInterface $request Request - */ - protected function addSslOptions(RequestInterface $request) - { - if ($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)) { - $this->setContextValue('ssl', 'verify_peer', true, true); - if ($cafile = $request->getCurlOptions()->get(CURLOPT_CAINFO)) { - $this->setContextValue('ssl', 'cafile', $cafile, true); - } - } else { - $this->setContextValue('ssl', 'verify_peer', false, true); - } - } - - /** - * Add body (content) specific options to the context options - * - * @param RequestInterface $request - */ - protected function addBodyOptions(RequestInterface $request) - { - // Add the content for the request if needed - if (!($request instanceof EntityEnclosingRequestInterface)) { - return; - } - - if (count($request->getPostFields())) { - $this->setContextValue('http', 'content', (string) $request->getPostFields(), true); - } elseif ($request->getBody()) { - $this->setContextValue('http', 'content', (string) $request->getBody(), true); - } - - // Always ensure a content-length header is sent - if (isset($this->contextOptions['http']['content'])) { - $headers = isset($this->contextOptions['http']['header']) ? $this->contextOptions['http']['header'] : array(); - $headers[] = 'Content-Length: ' . strlen($this->contextOptions['http']['content']); - $this->setContextValue('http', 'header', $headers, true); - } - } - - /** - * Add proxy parameters to the context if needed - * - * @param RequestInterface $request Request - */ - protected function addProxyOptions(RequestInterface $request) - { - if ($proxy = $request->getCurlOptions()->get(CURLOPT_PROXY)) { - $this->setContextValue('http', 'proxy', $proxy); - } - } - - /** - * Create the stream for the request with the context options - * - * @param array $params Parameters of the stream - * - * @return StreamInterface - */ - protected function createStream(array $params) - { - $http_response_header = null; - $url = $this->url; - $context = $this->context; - $fp = $this->createResource(function () use ($context, $url, &$http_response_header) { - return fopen((string) $url, 'r', false, $context); - }); - - // Determine the class to instantiate - $className = isset($params['stream_class']) ? $params['stream_class'] : __NAMESPACE__ . '\\Stream'; - - /** @var $stream StreamInterface */ - $stream = new $className($fp); - - // Track the response headers of the request - if (isset($http_response_header)) { - $this->lastResponseHeaders = $http_response_header; - $this->processResponseHeaders($stream); - } - - return $stream; - } - - /** - * Process response headers - * - * @param StreamInterface $stream - */ - protected function processResponseHeaders(StreamInterface $stream) - { - // Set the size on the stream if it was returned in the response - foreach ($this->lastResponseHeaders as $header) { - if ((stripos($header, 'Content-Length:')) === 0) { - $stream->setSize(trim(substr($header, 15))); - } - } - } - - /** - * Create a resource and check to ensure it was created successfully - * - * @param callable $callback Closure to invoke that must return a valid resource - * - * @return resource - * @throws RuntimeException on error - */ - protected function createResource($callback) - { - // Turn off error reporting while we try to initiate the request - $level = error_reporting(0); - $resource = call_user_func($callback); - error_reporting($level); - - // If the resource could not be created, then grab the last error and throw an exception - if (false === $resource) { - $message = 'Error creating resource. '; - foreach (error_get_last() as $key => $value) { - $message .= "[{$key}] {$value} "; - } - throw new RuntimeException(trim($message)); - } - - return $resource; - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php deleted file mode 100644 index 12bed268d..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Stream/Stream.php +++ /dev/null @@ -1,289 +0,0 @@ - array( - 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, - 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a+' => true - ), - 'write' => array( - 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+' => true, - 'wb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b' => true, - 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t' => true, 'a' => true, 'a+' => true - ) - ); - - /** - * @param resource $stream Stream resource to wrap - * @param int $size Size of the stream in bytes. Only pass if the size cannot be obtained from the stream. - * - * @throws InvalidArgumentException if the stream is not a stream resource - */ - public function __construct($stream, $size = null) - { - $this->setStream($stream, $size); - } - - /** - * Closes the stream when the helper is destructed - */ - public function __destruct() - { - $this->close(); - } - - public function __toString() - { - if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { - return ''; - } - - $originalPos = $this->ftell(); - $body = stream_get_contents($this->stream, -1, 0); - $this->seek($originalPos); - - return $body; - } - - public function close() - { - if (is_resource($this->stream)) { - fclose($this->stream); - } - $this->cache[self::IS_READABLE] = false; - $this->cache[self::IS_WRITABLE] = false; - } - - /** - * Calculate a hash of a Stream - * - * @param StreamInterface $stream Stream to calculate the hash for - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) - * @param bool $rawOutput Whether or not to use raw output - * - * @return bool|string Returns false on failure or a hash string on success - */ - public static function getHash(StreamInterface $stream, $algo, $rawOutput = false) - { - $pos = $stream->ftell(); - if (!$stream->seek(0)) { - return false; - } - - $ctx = hash_init($algo); - while (!$stream->feof()) { - hash_update($ctx, $stream->read(8192)); - } - - $out = hash_final($ctx, (bool) $rawOutput); - $stream->seek($pos); - - return $out; - } - - public function getMetaData($key = null) - { - $meta = stream_get_meta_data($this->stream); - - return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); - } - - public function getStream() - { - return $this->stream; - } - - public function setStream($stream, $size = null) - { - if (!is_resource($stream)) { - throw new InvalidArgumentException('Stream must be a resource'); - } - - $this->size = $size; - $this->stream = $stream; - $this->rebuildCache(); - - return $this; - } - - public function detachStream() - { - $this->stream = null; - - return $this; - } - - public function getWrapper() - { - return $this->cache[self::WRAPPER_TYPE]; - } - - public function getWrapperData() - { - return $this->getMetaData('wrapper_data') ?: array(); - } - - public function getStreamType() - { - return $this->cache[self::STREAM_TYPE]; - } - - public function getUri() - { - return $this->cache['uri']; - } - - public function getSize() - { - if ($this->size !== null) { - return $this->size; - } - - // If the stream is a file based stream and local, then use fstat - clearstatcache(true, $this->cache['uri']); - $stats = fstat($this->stream); - if (isset($stats['size'])) { - $this->size = $stats['size']; - return $this->size; - } elseif ($this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]) { - // Only get the size based on the content if the the stream is readable and seekable - $pos = $this->ftell(); - $this->size = strlen((string) $this); - $this->seek($pos); - return $this->size; - } - - return false; - } - - public function isReadable() - { - return $this->cache[self::IS_READABLE]; - } - - public function isRepeatable() - { - return $this->cache[self::IS_READABLE] && $this->cache[self::SEEKABLE]; - } - - public function isWritable() - { - return $this->cache[self::IS_WRITABLE]; - } - - public function isConsumed() - { - return feof($this->stream); - } - - public function feof() - { - return $this->isConsumed(); - } - - public function isLocal() - { - return $this->cache[self::IS_LOCAL]; - } - - public function isSeekable() - { - return $this->cache[self::SEEKABLE]; - } - - public function setSize($size) - { - $this->size = $size; - - return $this; - } - - public function seek($offset, $whence = SEEK_SET) - { - return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; - } - - public function read($length) - { - return fread($this->stream, $length); - } - - public function write($string) - { - // We can't know the size after writing anything - $this->size = null; - - return fwrite($this->stream, $string); - } - - public function ftell() - { - return ftell($this->stream); - } - - public function rewind() - { - return $this->seek(0); - } - - public function readLine($maxLength = null) - { - if (!$this->cache[self::IS_READABLE]) { - return false; - } else { - return $maxLength ? fgets($this->getStream(), $maxLength) : fgets($this->getStream()); - } - } - - public function setCustomData($key, $value) - { - $this->customData[$key] = $value; - - return $this; - } - - public function getCustomData($key) - { - return isset($this->customData[$key]) ? $this->customData[$key] : null; - } - - /** - * Reprocess stream metadata - */ - protected function rebuildCache() - { - $this->cache = stream_get_meta_data($this->stream); - $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); - $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); - $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); - } -} diff --git a/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php b/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php deleted file mode 100644 index 6d7dc3761..000000000 --- a/core/lib/guzzle/guzzle/src/Guzzle/Stream/StreamInterface.php +++ /dev/null @@ -1,218 +0,0 @@ -=5.3.2", - "guzzle/common": "self.version" - }, - "suggest": { - "guzzle/http": "To convert Guzzle request objects to PHP streams" - }, - "autoload": { - "psr-0": { "Guzzle\\Stream": "" } - }, - "target-dir": "Guzzle/Stream", - "extra": { - "branch-alias": { - "dev-master": "3.7-dev" - } - } -} diff --git a/core/lib/michelf/php-markdown/License.md b/core/lib/michelf/php-markdown/License.md deleted file mode 100644 index 027becbd5..000000000 --- a/core/lib/michelf/php-markdown/License.md +++ /dev/null @@ -1,36 +0,0 @@ -PHP Markdown Lib -Copyright (c) 2004-2013 Michel Fortin - -All rights reserved. - -Based on Markdown -Copyright (c) 2003-2006 John Gruber - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - -* Neither the name "Markdown" nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -This software is provided by the copyright holders and contributors "as -is" and any express or implied warranties, including, but not limited -to, the implied warranties of merchantability and fitness for a -particular purpose are disclaimed. In no event shall the copyright owner -or contributors be liable for any direct, indirect, incidental, special, -exemplary, or consequential damages (including, but not limited to, -procurement of substitute goods or services; loss of use, data, or -profits; or business interruption) however caused and on any theory of -liability, whether in contract, strict liability, or tort (including -negligence or otherwise) arising in any way out of the use of this -software, even if advised of the possibility of such damage. diff --git a/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php b/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php deleted file mode 100644 index 8c281094c..000000000 --- a/core/lib/michelf/php-markdown/Michelf/Markdown.inc.php +++ /dev/null @@ -1,10 +0,0 @@ - -# -# Original Markdown -# Copyright (c) 2004-2006 John Gruber -# -# -namespace Michelf; - - -# -# Markdown Parser Class -# - -class Markdown implements MarkdownInterface { - - ### Version ### - - const MARKDOWNLIB_VERSION = "1.4.0"; - - ### Simple Function Interface ### - - public static function defaultTransform($text) { - # - # Initialize the parser and return the result of its transform method. - # This will work fine for derived classes too. - # - # Take parser class on which this function was called. - $parser_class = \get_called_class(); - - # try to take parser from the static parser list - static $parser_list; - $parser =& $parser_list[$parser_class]; - - # create the parser it not already set - if (!$parser) - $parser = new $parser_class; - - # Transform text using parser. - return $parser->transform($text); - } - - ### Configuration Variables ### - - # Change to ">" for HTML output. - public $empty_element_suffix = " />"; - public $tab_width = 4; - - # Change to `true` to disallow markup or entities. - public $no_markup = false; - public $no_entities = false; - - # Predefined urls and titles for reference links and images. - public $predef_urls = array(); - public $predef_titles = array(); - - - ### Parser Implementation ### - - # Regex to match balanced [brackets]. - # Needed to insert a maximum bracked depth while converting to PHP. - protected $nested_brackets_depth = 6; - protected $nested_brackets_re; - - protected $nested_url_parenthesis_depth = 4; - protected $nested_url_parenthesis_re; - - # Table of hash values for escaped characters: - protected $escape_chars = '\`*_{}[]()>#+-.!'; - protected $escape_chars_re; - - - public function __construct() { - # - # Constructor function. Initialize appropriate member variables. - # - $this->_initDetab(); - $this->prepareItalicsAndBold(); - - $this->nested_brackets_re = - str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth). - str_repeat('\])*', $this->nested_brackets_depth); - - $this->nested_url_parenthesis_re = - str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth). - str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth); - - $this->escape_chars_re = '['.preg_quote($this->escape_chars).']'; - - # Sort document, block, and span gamut in ascendent priority order. - asort($this->document_gamut); - asort($this->block_gamut); - asort($this->span_gamut); - } - - - # Internal hashes used during transformation. - protected $urls = array(); - protected $titles = array(); - protected $html_hashes = array(); - - # Status flag to avoid invalid nesting. - protected $in_anchor = false; - - - protected function setup() { - # - # Called before the transformation process starts to setup parser - # states. - # - # Clear global hashes. - $this->urls = $this->predef_urls; - $this->titles = $this->predef_titles; - $this->html_hashes = array(); - - $this->in_anchor = false; - } - - protected function teardown() { - # - # Called after the transformation process to clear any variable - # which may be taking up memory unnecessarly. - # - $this->urls = array(); - $this->titles = array(); - $this->html_hashes = array(); - } - - - public function transform($text) { - # - # Main function. Performs some preprocessing on the input text - # and pass it through the document gamut. - # - $this->setup(); - - # Remove UTF-8 BOM and marker character in input, if present. - $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text); - - # Standardize line endings: - # DOS to Unix and Mac to Unix - $text = preg_replace('{\r\n?}', "\n", $text); - - # Make sure $text ends with a couple of newlines: - $text .= "\n\n"; - - # Convert all tabs to spaces. - $text = $this->detab($text); - - # Turn block-level HTML blocks into hash entries - $text = $this->hashHTMLBlocks($text); - - # Strip any lines consisting only of spaces and tabs. - # This makes subsequent regexen easier to write, because we can - # match consecutive blank lines with /\n+/ instead of something - # contorted like /[ ]*\n+/ . - $text = preg_replace('/^[ ]+$/m', '', $text); - - # Run document gamut methods. - foreach ($this->document_gamut as $method => $priority) { - $text = $this->$method($text); - } - - $this->teardown(); - - return $text . "\n"; - } - - protected $document_gamut = array( - # Strip link definitions, store in hashes. - "stripLinkDefinitions" => 20, - - "runBasicBlockGamut" => 30, - ); - - - protected function stripLinkDefinitions($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - $text = preg_replace_callback('{ - ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 - [ ]* - \n? # maybe *one* newline - [ ]* - (?: - <(.+?)> # url = $2 - | - (\S+?) # url = $3 - ) - [ ]* - \n? # maybe one newline - [ ]* - (?: - (?<=\s) # lookbehind for whitespace - ["(] - (.*?) # title = $4 - [")] - [ ]* - )? # title is optional - (?:\n+|\Z) - }xm', - array(&$this, '_stripLinkDefinitions_callback'), - $text); - return $text; - } - protected function _stripLinkDefinitions_callback($matches) { - $link_id = strtolower($matches[1]); - $url = $matches[2] == '' ? $matches[3] : $matches[2]; - $this->urls[$link_id] = $url; - $this->titles[$link_id] =& $matches[4]; - return ''; # String that will replace the block - } - - - protected function hashHTMLBlocks($text) { - if ($this->no_markup) return $text; - - $less_than_tab = $this->tab_width - 1; - - # Hashify HTML blocks: - # We only want to do this for block-level HTML tags, such as headers, - # lists, and tables. That's because we still want to wrap

    s around - # "paragraphs" that are wrapped in non-block-level tags, such as anchors, - # phrase emphasis, and spans. The list of tags we're looking for is - # hard-coded: - # - # * List "a" is made of tags which can be both inline or block-level. - # These will be treated block-level when the start tag is alone on - # its line, otherwise they're not matched here and will be taken as - # inline later. - # * List "b" is made of tags which are always block-level; - # - $block_tags_a_re = 'ins|del'; - $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. - 'script|noscript|form|fieldset|iframe|math|svg|'. - 'article|section|nav|aside|hgroup|header|footer|'. - 'figure'; - - # Regular expression for the content of a block tag. - $nested_tags_level = 4; - $attr = ' - (?> # optional tag attributes - \s # starts with whitespace - (?> - [^>"/]+ # text outside quotes - | - /+(?!>) # slash not followed by ">" - | - "[^"]*" # text inside double quotes (tolerate ">") - | - \'[^\']*\' # text inside single quotes (tolerate ">") - )* - )? - '; - $content = - str_repeat(' - (?> - [^<]+ # content without tag - | - <\2 # nested opening tag - '.$attr.' # attributes - (?> - /> - | - >', $nested_tags_level). # end of opening tag - '.*?'. # last level nested tag content - str_repeat(' - # closing nested tag - ) - | - <(?!/\2\s*> # other tags with a different name - ) - )*', - $nested_tags_level); - $content2 = str_replace('\2', '\3', $content); - - # First, look for nested blocks, e.g.: - #

    - #
    - # tags for inner block must be indented. - #
    - #
    - # - # The outermost tags must start at the left margin for this to match, and - # the inner nested divs must be indented. - # We need to do this before the next, more liberal match, because the next - # match will start at the first `
    ` and stop at the first `
    `. - $text = preg_replace_callback('{(?> - (?> - (?<=\n\n) # Starting after a blank line - | # or - \A\n? # the beginning of the doc - ) - ( # save in $1 - - # Match from `\n` to `\n`, handling nested tags - # in between. - - [ ]{0,'.$less_than_tab.'} - <('.$block_tags_b_re.')# start tag = $2 - '.$attr.'> # attributes followed by > and \n - '.$content.' # content, support nesting - # the matching end tag - [ ]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - - | # Special version for tags of group a. - - [ ]{0,'.$less_than_tab.'} - <('.$block_tags_a_re.')# start tag = $3 - '.$attr.'>[ ]*\n # attributes followed by > - '.$content2.' # content, support nesting - # the matching end tag - [ ]* # trailing spaces/tabs - (?=\n+|\Z) # followed by a newline or end of document - - | # Special case just for
    . It was easier to make a special - # case than to make the other regex more complicated. - - [ ]{0,'.$less_than_tab.'} - <(hr) # start tag = $2 - '.$attr.' # attributes - /?> # the matching end tag - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - | # Special case for standalone HTML comments: - - [ ]{0,'.$less_than_tab.'} - (?s: - - ) - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - | # PHP and ASP-style processor instructions ( - ) - [ ]* - (?=\n{2,}|\Z) # followed by a blank line or end of document - - ) - )}Sxmi', - array(&$this, '_hashHTMLBlocks_callback'), - $text); - - return $text; - } - protected function _hashHTMLBlocks_callback($matches) { - $text = $matches[1]; - $key = $this->hashBlock($text); - return "\n\n$key\n\n"; - } - - - protected function hashPart($text, $boundary = 'X') { - # - # Called whenever a tag must be hashed when a function insert an atomic - # element in the text stream. Passing $text to through this function gives - # a unique text-token which will be reverted back when calling unhash. - # - # The $boundary argument specify what character should be used to surround - # the token. By convension, "B" is used for block elements that needs not - # to be wrapped into paragraph tags at the end, ":" is used for elements - # that are word separators and "X" is used in the general case. - # - # Swap back any tag hash found in $text so we do not have to `unhash` - # multiple times at the end. - $text = $this->unhash($text); - - # Then hash the block. - static $i = 0; - $key = "$boundary\x1A" . ++$i . $boundary; - $this->html_hashes[$key] = $text; - return $key; # String that will replace the tag. - } - - - protected function hashBlock($text) { - # - # Shortcut function for hashPart with block-level boundaries. - # - return $this->hashPart($text, 'B'); - } - - - protected $block_gamut = array( - # - # These are all the transformations that form block-level - # tags like paragraphs, headers, and list items. - # - "doHeaders" => 10, - "doHorizontalRules" => 20, - - "doLists" => 40, - "doCodeBlocks" => 50, - "doBlockQuotes" => 60, - ); - - protected function runBlockGamut($text) { - # - # Run block gamut tranformations. - # - # We need to escape raw HTML in Markdown source before doing anything - # else. This need to be done for each block, and not only at the - # begining in the Markdown function since hashed blocks can be part of - # list items and could have been indented. Indented blocks would have - # been seen as a code block in a previous pass of hashHTMLBlocks. - $text = $this->hashHTMLBlocks($text); - - return $this->runBasicBlockGamut($text); - } - - protected function runBasicBlockGamut($text) { - # - # Run block gamut tranformations, without hashing HTML blocks. This is - # useful when HTML blocks are known to be already hashed, like in the first - # whole-document pass. - # - foreach ($this->block_gamut as $method => $priority) { - $text = $this->$method($text); - } - - # Finally form paragraph and restore hashed blocks. - $text = $this->formParagraphs($text); - - return $text; - } - - - protected function doHorizontalRules($text) { - # Do Horizontal Rules: - return preg_replace( - '{ - ^[ ]{0,3} # Leading space - ([-*_]) # $1: First marker - (?> # Repeated marker group - [ ]{0,2} # Zero, one, or two spaces. - \1 # Marker character - ){2,} # Group repeated at least twice - [ ]* # Tailing spaces - $ # End of line. - }mx', - "\n".$this->hashBlock("empty_element_suffix")."\n", - $text); - } - - - protected $span_gamut = array( - # - # These are all the transformations that occur *within* block-level - # tags like paragraphs, headers, and list items. - # - # Process character escapes, code spans, and inline HTML - # in one shot. - "parseSpan" => -30, - - # Process anchor and image tags. Images must come first, - # because ![foo][f] looks like an anchor. - "doImages" => 10, - "doAnchors" => 20, - - # Make links out of things like `` - # Must come after doAnchors, because you can use < and > - # delimiters in inline links like [this](). - "doAutoLinks" => 30, - "encodeAmpsAndAngles" => 40, - - "doItalicsAndBold" => 50, - "doHardBreaks" => 60, - ); - - protected function runSpanGamut($text) { - # - # Run span gamut tranformations. - # - foreach ($this->span_gamut as $method => $priority) { - $text = $this->$method($text); - } - - return $text; - } - - - protected function doHardBreaks($text) { - # Do hard breaks: - return preg_replace_callback('/ {2,}\n/', - array(&$this, '_doHardBreaks_callback'), $text); - } - protected function _doHardBreaks_callback($matches) { - return $this->hashPart("empty_element_suffix\n"); - } - - - protected function doAnchors($text) { - # - # Turn Markdown link shortcuts into XHTML tags. - # - if ($this->in_anchor) return $text; - $this->in_anchor = true; - - # - # First, handle reference-style links: [link text] [id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - # - # Next, inline-style links: [link text](url "optional title") - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - \( # literal paren - [ \n]* - (?: - <(.+?)> # href = $3 - | - ('.$this->nested_url_parenthesis_re.') # href = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # Title = $7 - \6 # matching quote - [ \n]* # ignore any spaces/tabs between closing quote and ) - )? # title is optional - \) - ) - }xs', - array(&$this, '_doAnchors_inline_callback'), $text); - - # - # Last, handle reference-style shortcuts: [link text] - # These must come last in case you've also got [link text][1] - # or [link text](/foo) - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ([^\[\]]+) # link text = $2; can\'t contain [ or ] - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - $this->in_anchor = false; - return $text; - } - protected function _doAnchors_reference_callback($matches) { - $whole_match = $matches[1]; - $link_text = $matches[2]; - $link_id =& $matches[3]; - - if ($link_id == "") { - # for shortcut links like [this][] or [this]. - $link_id = $link_text; - } - - # lower-case and turn embedded newlines into spaces - $link_id = strtolower($link_id); - $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); - - if (isset($this->urls[$link_id])) { - $url = $this->urls[$link_id]; - $url = $this->encodeAttribute($url); - - $result = "titles[$link_id] ) ) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - $result = $this->hashPart($result); - } - else { - $result = $whole_match; - } - return $result; - } - protected function _doAnchors_inline_callback($matches) { - $whole_match = $matches[1]; - $link_text = $this->runSpanGamut($matches[2]); - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - - $url = $this->encodeAttribute($url); - - $result = "encodeAttribute($title); - $result .= " title=\"$title\""; - } - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - - return $this->hashPart($result); - } - - - protected function doImages($text) { - # - # Turn Markdown image shortcuts into tags. - # - # - # First, handle reference-style labeled images: ![alt text][id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - - ) - }xs', - array(&$this, '_doImages_reference_callback'), $text); - - # - # Next, handle inline images: ![alt text](url "optional title") - # Don't forget: encode * and _ - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - \s? # One optional whitespace character - \( # literal paren - [ \n]* - (?: - <(\S*)> # src url = $3 - | - ('.$this->nested_url_parenthesis_re.') # src url = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # title = $7 - \6 # matching quote - [ \n]* - )? # title is optional - \) - ) - }xs', - array(&$this, '_doImages_inline_callback'), $text); - - return $text; - } - protected function _doImages_reference_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $link_id = strtolower($matches[3]); - - if ($link_id == "") { - $link_id = strtolower($alt_text); # for shortcut links like ![this][]. - } - - $alt_text = $this->encodeAttribute($alt_text); - if (isset($this->urls[$link_id])) { - $url = $this->encodeAttribute($this->urls[$link_id]); - $result = "\"$alt_text\"";titles[$link_id])) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - $result .= $this->empty_element_suffix; - $result = $this->hashPart($result); - } - else { - # If there's no such link ID, leave intact: - $result = $whole_match; - } - - return $result; - } - protected function _doImages_inline_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - - $alt_text = $this->encodeAttribute($alt_text); - $url = $this->encodeAttribute($url); - $result = "\"$alt_text\"";encodeAttribute($title); - $result .= " title=\"$title\""; # $title already quoted - } - $result .= $this->empty_element_suffix; - - return $this->hashPart($result); - } - - - protected function doHeaders($text) { - # Setext-style headers: - # Header 1 - # ======== - # - # Header 2 - # -------- - # - $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx', - array(&$this, '_doHeaders_callback_setext'), $text); - - # atx-style headers: - # # Header 1 - # ## Header 2 - # ## Header 2 with closing hashes ## - # ... - # ###### Header 6 - # - $text = preg_replace_callback('{ - ^(\#{1,6}) # $1 = string of #\'s - [ ]* - (.+?) # $2 = Header text - [ ]* - \#* # optional closing #\'s (not counted) - \n+ - }xm', - array(&$this, '_doHeaders_callback_atx'), $text); - - return $text; - } - protected function _doHeaders_callback_setext($matches) { - # Terrible hack to check we haven't found an empty list item. - if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1])) - return $matches[0]; - - $level = $matches[2]{0} == '=' ? 1 : 2; - $block = "".$this->runSpanGamut($matches[1]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - protected function _doHeaders_callback_atx($matches) { - $level = strlen($matches[1]); - $block = "".$this->runSpanGamut($matches[2]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - - - protected function doLists($text) { - # - # Form HTML ordered (numbered) and unordered (bulleted) lists. - # - $less_than_tab = $this->tab_width - 1; - - # Re-usable patterns to match list item bullets and number markers: - $marker_ul_re = '[*+-]'; - $marker_ol_re = '\d+[\.]'; - $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; - - $markers_relist = array( - $marker_ul_re => $marker_ol_re, - $marker_ol_re => $marker_ul_re, - ); - - foreach ($markers_relist as $marker_re => $other_marker_re) { - # Re-usable pattern to match any entirel ul or ol list: - $whole_list_re = ' - ( # $1 = whole list - ( # $2 - ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces - ('.$marker_re.') # $4 = first list item marker - [ ]+ - ) - (?s:.+?) - ( # $5 - \z - | - \n{2,} - (?=\S) - (?! # Negative lookahead for another list item marker - [ ]* - '.$marker_re.'[ ]+ - ) - | - (?= # Lookahead for another kind of list - \n - \3 # Must have the same indentation - '.$other_marker_re.'[ ]+ - ) - ) - ) - '; // mx - - # We use a different prefix before nested lists than top-level lists. - # See extended comment in _ProcessListItems(). - - if ($this->list_level) { - $text = preg_replace_callback('{ - ^ - '.$whole_list_re.' - }mx', - array(&$this, '_doLists_callback'), $text); - } - else { - $text = preg_replace_callback('{ - (?:(?<=\n)\n|\A\n?) # Must eat the newline - '.$whole_list_re.' - }mx', - array(&$this, '_doLists_callback'), $text); - } - } - - return $text; - } - protected function _doLists_callback($matches) { - # Re-usable patterns to match list item bullets and number markers: - $marker_ul_re = '[*+-]'; - $marker_ol_re = '\d+[\.]'; - $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)"; - - $list = $matches[1]; - $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol"; - - $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re ); - - $list .= "\n"; - $result = $this->processListItems($list, $marker_any_re); - - $result = $this->hashBlock("<$list_type>\n" . $result . ""); - return "\n". $result ."\n\n"; - } - - protected $list_level = 0; - - protected function processListItems($list_str, $marker_any_re) { - # - # Process the contents of a single ordered or unordered list, splitting it - # into individual list items. - # - # The $this->list_level global keeps track of when we're inside a list. - # Each time we enter a list, we increment it; when we leave a list, - # we decrement. If it's zero, we're not in a list anymore. - # - # We do this because when we're not inside a list, we want to treat - # something like this: - # - # I recommend upgrading to version - # 8. Oops, now this line is treated - # as a sub-list. - # - # As a single paragraph, despite the fact that the second line starts - # with a digit-period-space sequence. - # - # Whereas when we're inside a list (or sub-list), that line will be - # treated as the start of a sub-list. What a kludge, huh? This is - # an aspect of Markdown's syntax that's hard to parse perfectly - # without resorting to mind-reading. Perhaps the solution is to - # change the syntax rules such that sub-lists must start with a - # starting cardinal number; e.g. "1." or "a.". - - $this->list_level++; - - # trim trailing blank lines: - $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); - - $list_str = preg_replace_callback('{ - (\n)? # leading line = $1 - (^[ ]*) # leading whitespace = $2 - ('.$marker_any_re.' # list marker and space = $3 - (?:[ ]+|(?=\n)) # space only required if item is not empty - ) - ((?s:.*?)) # list item text = $4 - (?:(\n+(?=\n))|\n) # tailing blank line = $5 - (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n)))) - }xm', - array(&$this, '_processListItems_callback'), $list_str); - - $this->list_level--; - return $list_str; - } - protected function _processListItems_callback($matches) { - $item = $matches[4]; - $leading_line =& $matches[1]; - $leading_space =& $matches[2]; - $marker_space = $matches[3]; - $tailing_blank_line =& $matches[5]; - - if ($leading_line || $tailing_blank_line || - preg_match('/\n{2,}/', $item)) - { - # Replace marker with the appropriate whitespace indentation - $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item; - $item = $this->runBlockGamut($this->outdent($item)."\n"); - } - else { - # Recursion for sub-lists: - $item = $this->doLists($this->outdent($item)); - $item = preg_replace('/\n+$/', '', $item); - $item = $this->runSpanGamut($item); - } - - return "
  • " . $item . "
  • \n"; - } - - - protected function doCodeBlocks($text) { - # - # Process Markdown `
    ` blocks.
    -	#
    -		$text = preg_replace_callback('{
    -				(?:\n\n|\A\n?)
    -				(	            # $1 = the code block -- one or more lines, starting with a space/tab
    -				  (?>
    -					[ ]{'.$this->tab_width.'}  # Lines must start with a tab or a tab-width of spaces
    -					.*\n+
    -				  )+
    -				)
    -				((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
    -			}xm',
    -			array(&$this, '_doCodeBlocks_callback'), $text);
    -
    -		return $text;
    -	}
    -	protected function _doCodeBlocks_callback($matches) {
    -		$codeblock = $matches[1];
    -
    -		$codeblock = $this->outdent($codeblock);
    -		$codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
    -
    -		# trim leading newlines and trailing newlines
    -		$codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
    -
    -		$codeblock = "
    $codeblock\n
    "; - return "\n\n".$this->hashBlock($codeblock)."\n\n"; - } - - - protected function makeCodeSpan($code) { - # - # Create a code span markup for $code. Called from handleSpanToken. - # - $code = htmlspecialchars(trim($code), ENT_NOQUOTES); - return $this->hashPart("$code"); - } - - - protected $em_relist = array( - '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) { - foreach ($this->strong_relist as $strong => $strong_re) { - # Construct list of allowed token expressions. - $token_relist = array(); - if (isset($this->em_strong_relist["$em$strong"])) { - $token_relist[] = $this->em_strong_relist["$em$strong"]; - } - $token_relist[] = $em_re; - $token_relist[] = $strong_re; - - # Construct master expression from list. - $token_re = '{('. implode('|', $token_relist) .')}'; - $this->em_strong_prepared_relist["$em$strong"] = $token_re; - } - } - } - - protected function doItalicsAndBold($text) { - $token_stack = array(''); - $text_stack = array(''); - $em = ''; - $strong = ''; - $tree_char_em = false; - - while (1) { - # - # Get prepared regular expression for seraching emphasis tokens - # in current context. - # - $token_re = $this->em_strong_prepared_relist["$em$strong"]; - - # - # Each loop iteration search for the next emphasis token. - # Each token is then passed to handleSpanToken. - # - $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); - $text_stack[0] .= $parts[0]; - $token =& $parts[1]; - $text =& $parts[2]; - - if (empty($token)) { - # Reached end of text span: empty stack without emitting. - # any more emphasis. - while ($token_stack[0]) { - $text_stack[1] .= array_shift($token_stack); - $text_stack[0] .= array_shift($text_stack); - } - break; - } - - $token_len = strlen($token); - if ($tree_char_em) { - # Reached closing marker while inside a three-char emphasis. - if ($token_len == 3) { - # Three-char closing marker, close em and strong. - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $em = ''; - $strong = ''; - } else { - # Other closing marker: close one em or strong and - # change current token state to match the other - $token_stack[0] = str_repeat($token{0}, 3-$token_len); - $tag = $token_len == 2 ? "strong" : "em"; - $span = $text_stack[0]; - $span = $this->runSpanGamut($span); - $span = "<$tag>$span"; - $text_stack[0] = $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - $tree_char_em = false; - } else if ($token_len == 3) { - if ($em) { - # Reached closing marker for both em and strong. - # Closing strong marker: - for ($i = 0; $i < 2; ++$i) { - $shifted_token = array_shift($token_stack); - $tag = strlen($shifted_token) == 2 ? "strong" : "em"; - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "<$tag>$span"; - $text_stack[0] .= $this->hashPart($span); - $$tag = ''; # $$tag stands for $em or $strong - } - } else { - # Reached opening three-char emphasis marker. Push on token - # stack; will be handled by the special condition above. - $em = $token{0}; - $strong = "$em$em"; - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $tree_char_em = true; - } - } else if ($token_len == 2) { - if ($strong) { - # Unwind any dangling emphasis marker: - if (strlen($token_stack[0]) == 1) { - $text_stack[1] .= array_shift($token_stack); - $text_stack[0] .= array_shift($text_stack); - } - # Closing strong marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $strong = ''; - } else { - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $strong = $token; - } - } else { - # Here $token_len == 1 - if ($em) { - if (strlen($token_stack[0]) == 1) { - # Closing emphasis marker: - array_shift($token_stack); - $span = array_shift($text_stack); - $span = $this->runSpanGamut($span); - $span = "$span"; - $text_stack[0] .= $this->hashPart($span); - $em = ''; - } else { - $text_stack[0] .= $token; - } - } else { - array_unshift($token_stack, $token); - array_unshift($text_stack, ''); - $em = $token; - } - } - } - return $text_stack[0]; - } - - - protected function doBlockQuotes($text) { - $text = preg_replace_callback('/ - ( # Wrap whole match in $1 - (?> - ^[ ]*>[ ]? # ">" at the start of a line - .+\n # rest of the first line - (.+\n)* # subsequent consecutive lines - \n* # blanks - )+ - ) - /xm', - array(&$this, '_doBlockQuotes_callback'), $text); - - return $text; - } - protected function _doBlockQuotes_callback($matches) { - $bq = $matches[1]; - # trim one level of quoting - trim whitespace-only lines - $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq); - $bq = $this->runBlockGamut($bq); # recurse - - $bq = preg_replace('/^/m', " ", $bq); - # These leading spaces cause problem with
     content, 
    -		# so we need to fix that:
    -		$bq = preg_replace_callback('{(\s*
    .+?
    )}sx', - array(&$this, '_doBlockQuotes_callback2'), $bq); - - return "\n". $this->hashBlock("
    \n$bq\n
    ")."\n\n"; - } - protected function _doBlockQuotes_callback2($matches) { - $pre = $matches[1]; - $pre = preg_replace('/^ /m', '', $pre); - return $pre; - } - - - protected function formParagraphs($text) { - # - # Params: - # $text - string to process with html

    tags - # - # Strip leading and trailing lines: - $text = preg_replace('/\A\n+|\n+\z/', '', $text); - - $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); - - # - # Wrap

    tags and unhashify HTML blocks - # - foreach ($grafs as $key => $value) { - if (!preg_match('/^B\x1A[0-9]+B$/', $value)) { - # Is a paragraph. - $value = $this->runSpanGamut($value); - $value = preg_replace('/^([ ]*)/', "

    ", $value); - $value .= "

    "; - $grafs[$key] = $this->unhash($value); - } - else { - # Is a block. - # Modify elements of @grafs in-place... - $graf = $value; - $block = $this->html_hashes[$graf]; - $graf = $block; -// if (preg_match('{ -// \A -// ( # $1 =
    tag -//
    ]* -// \b -// markdown\s*=\s* ([\'"]) # $2 = attr quote char -// 1 -// \2 -// [^>]* -// > -// ) -// ( # $3 = contents -// .* -// ) -// (
    ) # $4 = closing tag -// \z -// }xs', $block, $matches)) -// { -// list(, $div_open, , $div_content, $div_close) = $matches; -// -// # We can't call Markdown(), because that resets the hash; -// # that initialization code should be pulled into its own sub, though. -// $div_content = $this->hashHTMLBlocks($div_content); -// -// # Run document gamut methods on the content. -// foreach ($this->document_gamut as $method => $priority) { -// $div_content = $this->$method($div_content); -// } -// -// $div_open = preg_replace( -// '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open); -// -// $graf = $div_open . "\n" . $div_content . "\n" . $div_close; -// } - $grafs[$key] = $graf; - } - } - - return implode("\n\n", $grafs); - } - - - protected function encodeAttribute($text) { - # - # Encode text for a double-quoted HTML attribute. This function - # is *not* suitable for attributes enclosed in single quotes. - # - $text = $this->encodeAmpsAndAngles($text); - $text = str_replace('"', '"', $text); - return $text; - } - - - protected function encodeAmpsAndAngles($text) { - # - # Smart processing for ampersands and angle brackets that need to - # be encoded. Valid character entities are left alone unless the - # no-entities mode is set. - # - if ($this->no_entities) { - $text = str_replace('&', '&', $text); - } else { - # Ampersand-encoding based entirely on Nat Irons's Amputator - # MT plugin: - $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/', - '&', $text); - } - # Encode remaining <'s - $text = str_replace('<', '<', $text); - - return $text; - } - - - protected function doAutoLinks($text) { - $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i', - array(&$this, '_doAutoLinks_url_callback'), $text); - - # Email addresses: - $text = preg_replace_callback('{ - < - (?:mailto:)? - ( - (?: - [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+ - | - ".*?" - ) - \@ - (?: - [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+ - | - \[[\d.a-fA-F:]+\] # IPv4 & IPv6 - ) - ) - > - }xi', - array(&$this, '_doAutoLinks_email_callback'), $text); - $text = preg_replace_callback('{<(tel:([^\'">\s]+))>}i',array(&$this, '_doAutoLinks_tel_callback'), $text); - - return $text; - } - protected function _doAutoLinks_tel_callback($matches) { - $url = $this->encodeAttribute($matches[1]); - $tel = $this->encodeAttribute($matches[2]); - $link = "$tel"; - return $this->hashPart($link); - } - protected function _doAutoLinks_url_callback($matches) { - $url = $this->encodeAttribute($matches[1]); - $link = "$url"; - return $this->hashPart($link); - } - protected function _doAutoLinks_email_callback($matches) { - $address = $matches[1]; - $link = $this->encodeEmailAddress($address); - return $this->hashPart($link); - } - - - protected function encodeEmailAddress($addr) { - # - # Input: an email address, e.g. "foo@example.com" - # - # Output: the email address as a mailto link, with each character - # of the address encoded as either a decimal or hex entity, in - # the hopes of foiling most address harvesting spam bots. E.g.: - # - #

    foo@exampl - # e.com

    - # - # Based by a filter by Matthew Wickline, posted to BBEdit-Talk. - # With some optimizations by Milian Wolff. - # - $addr = "mailto:" . $addr; - $chars = preg_split('/(? $char) { - $ord = ord($char); - # Ignore non-ascii chars. - if ($ord < 128) { - $r = ($seed * (1 + $key)) % 100; # Pseudo-random function. - # roughly 10% raw, 45% hex, 45% dec - # '@' *must* be encoded. I insist. - if ($r > 90 && $char != '@') /* do nothing */; - else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';'; - else $chars[$key] = '&#'.$ord.';'; - } - } - - $addr = implode('', $chars); - $text = implode('', array_slice($chars, 7)); # text without `mailto:` - $addr = "$text"; - - return $addr; - } - - - protected function parseSpan($str) { - # - # Take the string $str and parse it into tokens, hashing embeded HTML, - # escaped characters and handling code spans. - # - $output = ''; - - $span_re = '{ - ( - \\\\'.$this->escape_chars_re.' - | - (?no_markup ? '' : ' - | - # comment - | - <\?.*?\?> | <%.*?%> # processing instruction - | - <[!$]?[-a-zA-Z0-9:_]+ # regular tags - (?> - \s - (?>[^"\'>]+|"[^"]*"|\'[^\']*\')* - )? - > - | - <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag - | - # closing tag - ').' - ) - }xs'; - - while (1) { - # - # Each loop iteration seach for either the next tag, the next - # openning code span marker, or the next escaped character. - # Each token is then passed to handleSpanToken. - # - $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE); - - # Create token from text preceding tag. - if ($parts[0] != "") { - $output .= $parts[0]; - } - - # Check if we reach the end. - if (isset($parts[1])) { - $output .= $this->handleSpanToken($parts[1], $parts[2]); - $str = $parts[2]; - } - else { - break; - } - } - - return $output; - } - - - protected function handleSpanToken($token, &$str) { - # - # Handle $token provided by parseSpan by determining its nature and - # returning the corresponding value that should replace it. - # - switch ($token{0}) { - case "\\": - return $this->hashPart("&#". ord($token{1}). ";"); - case "`": - # Search for end marker in remaining text. - if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm', - $str, $matches)) - { - $str = $matches[2]; - $codespan = $this->makeCodeSpan($matches[1]); - return $this->hashPart($codespan); - } - return $token; // return as text since no ending marker found. - default: - return $this->hashPart($token); - } - } - - - protected function outdent($text) { - # - # Remove one level of line-leading tabs or spaces - # - return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text); - } - - - # String length function for detab. `_initDetab` will create a function to - # hanlde UTF-8 if the default function does not exist. - protected $utf8_strlen = 'mb_strlen'; - - protected function detab($text) { - # - # Replace tabs with the appropriate amount of space. - # - # For each line we separate the line in blocks delemited by - # tab characters. Then we reconstruct every line by adding the - # appropriate number of space between each blocks. - - $text = preg_replace_callback('/^.*\t.*$/m', - array(&$this, '_detab_callback'), $text); - - return $text; - } - protected function _detab_callback($matches) { - $line = $matches[0]; - $strlen = $this->utf8_strlen; # strlen function for UTF-8. - - # Split in blocks. - $blocks = explode("\t", $line); - # Add each blocks to the line. - $line = $blocks[0]; - unset($blocks[0]); # Do not add first block twice. - foreach ($blocks as $block) { - # Calculate amount of space, insert spaces, insert block. - $amount = $this->tab_width - - $strlen($line, 'UTF-8') % $this->tab_width; - $line .= str_repeat(" ", $amount) . $block; - } - return $line; - } - protected function _initDetab() { - # - # Check for the availability of the function in the `utf8_strlen` property - # (initially `mb_strlen`). If the function is not available, create a - # function that will loosely count the number of UTF-8 characters with a - # regular expression. - # - if (function_exists($this->utf8_strlen)) return; - $this->utf8_strlen = create_function('$text', 'return preg_match_all( - "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/", - $text, $m);'); - } - - - protected function unhash($text) { - # - # Swap back in all the tags hashed by _HashHTMLBlocks. - # - return preg_replace_callback('/(.)\x1A[0-9]+\1/', - array(&$this, '_unhash_callback'), $text); - } - protected function _unhash_callback($matches) { - return $this->html_hashes[$matches[0]]; - } - -} - - -# -# Temporary Markdown Extra Parser Implementation Class -# -# NOTE: DON'T USE THIS CLASS -# Currently the implementation of of Extra resides here in this temporary class. -# This makes it easier to propagate the changes between the three different -# packaging styles of PHP Markdown. When this issue is resolved, this -# MarkdownExtra_TmpImpl class here will disappear and \Michelf\MarkdownExtra -# will contain the code. So please use \Michelf\MarkdownExtra and ignore this -# one. -# - -abstract class _MarkdownExtra_TmpImpl extends \Michelf\Markdown { - - ### Configuration Variables ### - - # Prefix for footnote ids. - public $fn_id_prefix = ""; - - # Optional title attribute for footnote links and backlinks. - public $fn_link_title = ""; - public $fn_backlink_title = ""; - - # Optional class attribute for footnote links and backlinks. - public $fn_link_class = "footnote-ref"; - public $fn_backlink_class = "footnote-backref"; - - # Class name for table cell alignment (%% replaced left/center/right) - # For instance: 'go-%%' becomes 'go-left' or 'go-right' or 'go-center' - # If empty, the align attribute is used instead of a class name. - public $table_align_class_tmpl = ''; - - # Optional class prefix for fenced code block. - public $code_class_prefix = ""; - # Class attribute for code blocks goes on the `code` tag; - # setting this to true will put attributes on the `pre` tag instead. - public $code_attr_on_pre = false; - - # Predefined abbreviations. - public $predef_abbr = array(); - - - ### Parser Implementation ### - - public function __construct() { - # - # Constructor function. Initialize the parser object. - # - # Add extra escapable characters before parent constructor - # initialize the table. - $this->escape_chars .= ':|'; - - # Insert extra document, block, and span transformations. - # Parent constructor will do the sorting. - $this->document_gamut += array( - "doFencedCodeBlocks" => 5, - "stripFootnotes" => 15, - "stripAbbreviations" => 25, - "appendFootnotes" => 50, - ); - $this->block_gamut += array( - "doFencedCodeBlocks" => 5, - "doTables" => 15, - "doDefLists" => 45, - ); - $this->span_gamut += array( - "doFootnotes" => 5, - "doAbbreviations" => 70, - ); - - parent::__construct(); - } - - - # Extra variables used during extra transformations. - protected $footnotes = array(); - protected $footnotes_ordered = array(); - protected $footnotes_ref_count = array(); - protected $footnotes_numbers = array(); - protected $abbr_desciptions = array(); - protected $abbr_word_re = ''; - - # Give the current footnote number. - protected $footnote_counter = 1; - - - protected function setup() { - # - # Setting up Extra-specific variables. - # - parent::setup(); - - $this->footnotes = array(); - $this->footnotes_ordered = array(); - $this->footnotes_ref_count = array(); - $this->footnotes_numbers = array(); - $this->abbr_desciptions = array(); - $this->abbr_word_re = ''; - $this->footnote_counter = 1; - - foreach ($this->predef_abbr as $abbr_word => $abbr_desc) { - if ($this->abbr_word_re) - $this->abbr_word_re .= '|'; - $this->abbr_word_re .= preg_quote($abbr_word); - $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); - } - } - - protected function teardown() { - # - # Clearing Extra-specific variables. - # - $this->footnotes = array(); - $this->footnotes_ordered = array(); - $this->footnotes_ref_count = array(); - $this->footnotes_numbers = array(); - $this->abbr_desciptions = array(); - $this->abbr_word_re = ''; - - parent::teardown(); - } - - - ### Extra Attribute Parser ### - - # Expression to use to catch attributes (includes the braces) - protected $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}'; - # Expression to use when parsing in a context when no capture is desired - protected $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}'; - - protected function doExtraAttributes($tag_name, $attr) { - # - # Parse attributes caught by the $this->id_class_attr_catch_re expression - # and return the HTML-formatted list of attributes. - # - # Currently supported attributes are .class and #id. - # - if (empty($attr)) return ""; - - # Split on components - preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches); - $elements = $matches[0]; - - # handle classes and ids (only first id taken into account) - $classes = array(); - $id = false; - foreach ($elements as $element) { - if ($element{0} == '.') { - $classes[] = substr($element, 1); - } else if ($element{0} == '#') { - if ($id === false) $id = substr($element, 1); - } - } - - # compose attributes as string - $attr_str = ""; - if (!empty($id)) { - $attr_str .= ' id="'.$id.'"'; - } - if (!empty($classes)) { - $attr_str .= ' class="'.implode(" ", $classes).'"'; - } - return $attr_str; - } - - - protected function stripLinkDefinitions($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: ^[id]: url "optional title" - $text = preg_replace_callback('{ - ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1 - [ ]* - \n? # maybe *one* newline - [ ]* - (?: - <(.+?)> # url = $2 - | - (\S+?) # url = $3 - ) - [ ]* - \n? # maybe one newline - [ ]* - (?: - (?<=\s) # lookbehind for whitespace - ["(] - (.*?) # title = $4 - [")] - [ ]* - )? # title is optional - (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr - (?:\n+|\Z) - }xm', - array(&$this, '_stripLinkDefinitions_callback'), - $text); - return $text; - } - protected function _stripLinkDefinitions_callback($matches) { - $link_id = strtolower($matches[1]); - $url = $matches[2] == '' ? $matches[3] : $matches[2]; - $this->urls[$link_id] = $url; - $this->titles[$link_id] =& $matches[4]; - $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]); - return ''; # String that will replace the block - } - - - ### HTML Block Parser ### - - # Tags that are always treated as block tags: - protected $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption'; - - # Tags treated as block tags only if the opening tag is alone on its line: - protected $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video'; - - # Tags where markdown="1" default to span mode: - protected $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address'; - - # Tags which must not have their contents modified, no matter where - # they appear: - protected $clean_tags_re = 'script|math|svg'; - - # Tags that do not need to be closed. - protected $auto_close_tags_re = 'hr|img|param|source|track'; - - - protected function hashHTMLBlocks($text) { - # - # Hashify HTML Blocks and "clean tags". - # - # We only want to do this for block-level HTML tags, such as headers, - # lists, and tables. That's because we still want to wrap

    s around - # "paragraphs" that are wrapped in non-block-level tags, such as anchors, - # phrase emphasis, and spans. The list of tags we're looking for is - # hard-coded. - # - # This works by calling _HashHTMLBlocks_InMarkdown, which then calls - # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1" - # attribute is found within a tag, _HashHTMLBlocks_InHTML calls back - # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag. - # These two functions are calling each other. It's recursive! - # - if ($this->no_markup) return $text; - - # - # Call the HTML-in-Markdown hasher. - # - list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text); - - return $text; - } - protected function _hashHTMLBlocks_inMarkdown($text, $indent = 0, - $enclosing_tag_re = '', $span = false) - { - # - # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags. - # - # * $indent is the number of space to be ignored when checking for code - # blocks. This is important because if we don't take the indent into - # account, something like this (which looks right) won't work as expected: - # - #

    - #
    - # Hello World. <-- Is this a Markdown code block or text? - #
    <-- Is this a Markdown code block or a real tag? - #
    - # - # If you don't like this, just don't indent the tag on which - # you apply the markdown="1" attribute. - # - # * If $enclosing_tag_re is not empty, stops at the first unmatched closing - # tag with that name. Nested tags supported. - # - # * If $span is true, text inside must treated as span. So any double - # newline will be replaced by a single newline so that it does not create - # paragraphs. - # - # Returns an array of that form: ( processed text , remaining text ) - # - if ($text === '') return array('', ''); - - # Regex to check for the presense of newlines around a block tag. - $newline_before_re = '/(?:^\n?|\n\n)*$/'; - $newline_after_re = - '{ - ^ # Start of text following the tag. - (?>[ ]*)? # Optional comment. - [ ]*\n # Must be followed by newline. - }xs'; - - # Regex to match any tag. - $block_tag_re = - '{ - ( # $2: Capture whole tag. - # Tag name. - '.$this->block_tags_re.' | - '.$this->context_block_tags_re.' | - '.$this->clean_tags_re.' | - (?!\s)'.$enclosing_tag_re.' - ) - (?: - (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name. - (?> - ".*?" | # Double quotes (can contain `>`) - \'.*?\' | # Single quotes (can contain `>`) - .+? # Anything but quotes and `>`. - )*? - )? - > # End of tag. - | - # HTML Comment - | - <\?.*?\?> | <%.*?%> # Processing instruction - | - # CData Block - '. ( !$span ? ' # If not in span. - | - # Indented code block - (?: ^[ ]*\n | ^ | \n[ ]*\n ) - [ ]{'.($indent+4).'}[^\n]* \n - (?> - (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n - )* - | - # Fenced code block marker - (?<= ^ | \n ) - [ ]{0,'.($indent+3).'}(?:~{3,}|`{3,}) - [ ]* - (?: - \.?[-_:a-zA-Z0-9]+ # standalone class name - | - '.$this->id_class_attr_nocatch_re.' # extra attributes - )? - [ ]* - (?= \n ) - ' : '' ). ' # End (if not is span). - | - # Code span marker - # Note, this regex needs to go after backtick fenced - # code blocks but it should also be kept outside of the - # "if not in span" condition adding backticks to the parser - `+ - ) - }xs'; - - - $depth = 0; # Current depth inside the tag tree. - $parsed = ""; # Parsed text that will be returned. - - # - # Loop through every tag until we find the closing tag of the parent - # or loop until reaching the end of text if no parent tag specified. - # - do { - # - # Split the text using the first $tag_match pattern found. - # Text before pattern will be first in the array, text after - # pattern will be at the end, and between will be any catches made - # by the pattern. - # - $parts = preg_split($block_tag_re, $text, 2, - PREG_SPLIT_DELIM_CAPTURE); - - # If in Markdown span mode, add a empty-string span-level hash - # after each newline to prevent triggering any block element. - if ($span) { - $void = $this->hashPart("", ':'); - $newline = "$void\n"; - $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void; - } - - $parsed .= $parts[0]; # Text before current tag. - - # If end of $text has been reached. Stop loop. - if (count($parts) < 3) { - $text = ""; - break; - } - - $tag = $parts[1]; # Tag to handle. - $text = $parts[2]; # Remaining text after current tag. - $tag_re = preg_quote($tag); # For use in a regular expression. - - # - # Check for: Fenced code block marker. - # Note: need to recheck the whole tag to disambiguate backtick - # fences from code spans - # - if (preg_match('{^\n?([ ]{0,'.($indent+3).'})(~{3,}|`{3,})[ ]*(?:\.?[-_:a-zA-Z0-9]+|'.$this->id_class_attr_nocatch_re.')?[ ]*\n?$}', $tag, $capture)) { - # Fenced code block marker: find matching end marker. - $fence_indent = strlen($capture[1]); # use captured indent in re - $fence_re = $capture[2]; # use captured fence in re - if (preg_match('{^(?>.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text, - $matches)) - { - # End marker found: pass text unchanged until marker. - $parsed .= $tag . $matches[0]; - $text = substr($text, strlen($matches[0])); - } - else { - # No end marker: just skip it. - $parsed .= $tag; - } - } - # - # Check for: Indented code block. - # - else if ($tag{0} == "\n" || $tag{0} == " ") { - # Indented code block: pass it unchanged, will be handled - # later. - $parsed .= $tag; - } - # - # Check for: Code span marker - # Note: need to check this after backtick fenced code blocks - # - else if ($tag{0} == "`") { - # Find corresponding end marker. - $tag_re = preg_quote($tag); - if (preg_match('{^(?>.+?|\n(?!\n))*?(?block_tags_re.')\b}', $tag) || - ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) && - preg_match($newline_before_re, $parsed) && - preg_match($newline_after_re, $text) ) - ) - { - # Need to parse tag and following text using the HTML parser. - list($block_text, $text) = - $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true); - - # Make sure it stays outside of any paragraph by adding newlines. - $parsed .= "\n\n$block_text\n\n"; - } - # - # Check for: Clean tag (like script, math) - # HTML Comments, processing instructions. - # - else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) || - $tag{1} == '!' || $tag{1} == '?') - { - # Need to parse tag and following text using the HTML parser. - # (don't check for markdown attribute) - list($block_text, $text) = - $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false); - - $parsed .= $block_text; - } - # - # Check for: Tag with same name as enclosing tag. - # - else if ($enclosing_tag_re !== '' && - # Same name as enclosing tag. - preg_match('{^= 0); - - return array($parsed, $text); - } - protected function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) { - # - # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags. - # - # * Calls $hash_method to convert any blocks. - # * Stops when the first opening tag closes. - # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed. - # (it is not inside clean tags) - # - # Returns an array of that form: ( processed text , remaining text ) - # - if ($text === '') return array('', ''); - - # Regex to match `markdown` attribute inside of a tag. - $markdown_attr_re = ' - { - \s* # Eat whitespace before the `markdown` attribute - markdown - \s*=\s* - (?> - (["\']) # $1: quote delimiter - (.*?) # $2: attribute value - \1 # matching delimiter - | - ([^\s>]*) # $3: unquoted attribute value - ) - () # $4: make $3 always defined (avoid warnings) - }xs'; - - # Regex to match any tag. - $tag_re = '{ - ( # $2: Capture whole tag. - - ".*?" | # Double quotes (can contain `>`) - \'.*?\' | # Single quotes (can contain `>`) - .+? # Anything but quotes and `>`. - )*? - )? - > # End of tag. - | - # HTML Comment - | - <\?.*?\?> | <%.*?%> # Processing instruction - | - # CData Block - ) - }xs'; - - $original_text = $text; # Save original text in case of faliure. - - $depth = 0; # Current depth inside the tag tree. - $block_text = ""; # Temporary text holder for current text. - $parsed = ""; # Parsed text that will be returned. - - # - # Get the name of the starting tag. - # (This pattern makes $base_tag_name_re safe without quoting.) - # - if (preg_match('/^<([\w:$]*)\b/', $text, $matches)) - $base_tag_name_re = $matches[1]; - - # - # Loop through every tag until we find the corresponding closing tag. - # - do { - # - # Split the text using the first $tag_match pattern found. - # Text before pattern will be first in the array, text after - # pattern will be at the end, and between will be any catches made - # by the pattern. - # - $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE); - - if (count($parts) < 3) { - # - # End of $text reached with unbalenced tag(s). - # In that case, we return original text unchanged and pass the - # first character as filtered to prevent an infinite loop in the - # parent function. - # - return array($original_text{0}, substr($original_text, 1)); - } - - $block_text .= $parts[0]; # Text before current tag. - $tag = $parts[1]; # Tag to handle. - $text = $parts[2]; # Remaining text after current tag. - - # - # Check for: Auto-close tag (like
    ) - # Comments and Processing Instructions. - # - if (preg_match('{^auto_close_tags_re.')\b}', $tag) || - $tag{1} == '!' || $tag{1} == '?') - { - # Just add the tag to the block as if it was text. - $block_text .= $tag; - } - else { - # - # Increase/decrease nested tag count. Only do so if - # the tag's name match base tag's. - # - if (preg_match('{^mode = $attr_m[2] . $attr_m[3]; - $span_mode = $this->mode == 'span' || $this->mode != 'block' && - preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag); - - # Calculate indent before tag. - if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) { - $strlen = $this->utf8_strlen; - $indent = $strlen($matches[1], 'UTF-8'); - } else { - $indent = 0; - } - - # End preceding block with this tag. - $block_text .= $tag; - $parsed .= $this->$hash_method($block_text); - - # Get enclosing tag name for the ParseMarkdown function. - # (This pattern makes $tag_name_re safe without quoting.) - preg_match('/^<([\w:$]*)\b/', $tag, $matches); - $tag_name_re = $matches[1]; - - # Parse the content using the HTML-in-Markdown parser. - list ($block_text, $text) - = $this->_hashHTMLBlocks_inMarkdown($text, $indent, - $tag_name_re, $span_mode); - - # Outdent markdown text. - if ($indent > 0) { - $block_text = preg_replace("/^[ ]{1,$indent}/m", "", - $block_text); - } - - # Append tag content to parsed text. - if (!$span_mode) $parsed .= "\n\n$block_text\n\n"; - else $parsed .= "$block_text"; - - # Start over with a new block. - $block_text = ""; - } - else $block_text .= $tag; - } - - } while ($depth > 0); - - # - # Hash last block text that wasn't processed inside the loop. - # - $parsed .= $this->$hash_method($block_text); - - return array($parsed, $text); - } - - - protected function hashClean($text) { - # - # Called whenever a tag must be hashed when a function inserts a "clean" tag - # in $text, it passes through this function and is automaticaly escaped, - # blocking invalid nested overlap. - # - return $this->hashPart($text, 'C'); - } - - - protected function doAnchors($text) { - # - # Turn Markdown link shortcuts into XHTML tags. - # - if ($this->in_anchor) return $text; - $this->in_anchor = true; - - # - # First, handle reference-style links: [link text] [id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - # - # Next, inline-style links: [link text](url "optional title") - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ('.$this->nested_brackets_re.') # link text = $2 - \] - \( # literal paren - [ \n]* - (?: - <(.+?)> # href = $3 - | - ('.$this->nested_url_parenthesis_re.') # href = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # Title = $7 - \6 # matching quote - [ \n]* # ignore any spaces/tabs between closing quote and ) - )? # title is optional - \) - (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes - ) - }xs', - array(&$this, '_doAnchors_inline_callback'), $text); - - # - # Last, handle reference-style shortcuts: [link text] - # These must come last in case you've also got [link text][1] - # or [link text](/foo) - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - \[ - ([^\[\]]+) # link text = $2; can\'t contain [ or ] - \] - ) - }xs', - array(&$this, '_doAnchors_reference_callback'), $text); - - $this->in_anchor = false; - return $text; - } - protected function _doAnchors_reference_callback($matches) { - $whole_match = $matches[1]; - $link_text = $matches[2]; - $link_id =& $matches[3]; - - if ($link_id == "") { - # for shortcut links like [this][] or [this]. - $link_id = $link_text; - } - - # lower-case and turn embedded newlines into spaces - $link_id = strtolower($link_id); - $link_id = preg_replace('{[ ]?\n}', ' ', $link_id); - - if (isset($this->urls[$link_id])) { - $url = $this->urls[$link_id]; - $url = $this->encodeAttribute($url); - - $result = "titles[$link_id] ) ) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - if (isset($this->ref_attr[$link_id])) - $result .= $this->ref_attr[$link_id]; - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - $result = $this->hashPart($result); - } - else { - $result = $whole_match; - } - return $result; - } - protected function _doAnchors_inline_callback($matches) { - $whole_match = $matches[1]; - $link_text = $this->runSpanGamut($matches[2]); - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); - - - $url = $this->encodeAttribute($url); - - $result = "encodeAttribute($title); - $result .= " title=\"$title\""; - } - $result .= $attr; - - $link_text = $this->runSpanGamut($link_text); - $result .= ">$link_text"; - - return $this->hashPart($result); - } - - - protected function doImages($text) { - # - # Turn Markdown image shortcuts into tags. - # - # - # First, handle reference-style labeled images: ![alt text][id] - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - - [ ]? # one optional space - (?:\n[ ]*)? # one optional newline followed by spaces - - \[ - (.*?) # id = $3 - \] - - ) - }xs', - array(&$this, '_doImages_reference_callback'), $text); - - # - # Next, handle inline images: ![alt text](url "optional title") - # Don't forget: encode * and _ - # - $text = preg_replace_callback('{ - ( # wrap whole match in $1 - !\[ - ('.$this->nested_brackets_re.') # alt text = $2 - \] - \s? # One optional whitespace character - \( # literal paren - [ \n]* - (?: - <(\S*)> # src url = $3 - | - ('.$this->nested_url_parenthesis_re.') # src url = $4 - ) - [ \n]* - ( # $5 - ([\'"]) # quote char = $6 - (.*?) # title = $7 - \6 # matching quote - [ \n]* - )? # title is optional - \) - (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes - ) - }xs', - array(&$this, '_doImages_inline_callback'), $text); - - return $text; - } - protected function _doImages_reference_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $link_id = strtolower($matches[3]); - - if ($link_id == "") { - $link_id = strtolower($alt_text); # for shortcut links like ![this][]. - } - - $alt_text = $this->encodeAttribute($alt_text); - if (isset($this->urls[$link_id])) { - $url = $this->encodeAttribute($this->urls[$link_id]); - $result = "\"$alt_text\"";titles[$link_id])) { - $title = $this->titles[$link_id]; - $title = $this->encodeAttribute($title); - $result .= " title=\"$title\""; - } - if (isset($this->ref_attr[$link_id])) - $result .= $this->ref_attr[$link_id]; - $result .= $this->empty_element_suffix; - $result = $this->hashPart($result); - } - else { - # If there's no such link ID, leave intact: - $result = $whole_match; - } - - return $result; - } - protected function _doImages_inline_callback($matches) { - $whole_match = $matches[1]; - $alt_text = $matches[2]; - $url = $matches[3] == '' ? $matches[4] : $matches[3]; - $title =& $matches[7]; - $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); - - $alt_text = $this->encodeAttribute($alt_text); - $url = $this->encodeAttribute($url); - $result = "\"$alt_text\"";encodeAttribute($title); - $result .= " title=\"$title\""; # $title already quoted - } - $result .= $attr; - $result .= $this->empty_element_suffix; - - return $this->hashPart($result); - } - - - protected function doHeaders($text) { - # - # Redefined to add id and class attribute support. - # - # Setext-style headers: - # Header 1 {#header1} - # ======== - # - # Header 2 {#header2 .class1 .class2} - # -------- - # - $text = preg_replace_callback( - '{ - (^.+?) # $1: Header text - (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes - [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer - }mx', - array(&$this, '_doHeaders_callback_setext'), $text); - - # atx-style headers: - # # Header 1 {#header1} - # ## Header 2 {#header2} - # ## Header 2 with closing hashes ## {#header3.class1.class2} - # ... - # ###### Header 6 {.class2} - # - $text = preg_replace_callback('{ - ^(\#{1,6}) # $1 = string of #\'s - [ ]* - (.+?) # $2 = Header text - [ ]* - \#* # optional closing #\'s (not counted) - (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes - [ ]* - \n+ - }xm', - array(&$this, '_doHeaders_callback_atx'), $text); - - return $text; - } - protected function _doHeaders_callback_setext($matches) { - if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) - return $matches[0]; - $level = $matches[3]{0} == '=' ? 1 : 2; - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]); - $block = "".$this->runSpanGamut($matches[1]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - protected function _doHeaders_callback_atx($matches) { - $level = strlen($matches[1]); - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]); - $block = "".$this->runSpanGamut($matches[2]).""; - return "\n" . $this->hashBlock($block) . "\n\n"; - } - - - protected function doTables($text) { - # - # Form HTML tables. - # - $less_than_tab = $this->tab_width - 1; - # - # Find tables with leading pipe. - # - # | Header 1 | Header 2 - # | -------- | -------- - # | Cell 1 | Cell 2 - # | Cell 3 | Cell 4 - # - $text = preg_replace_callback(' - { - ^ # Start of a line - [ ]{0,'.$less_than_tab.'} # Allowed whitespace. - [|] # Optional leading pipe (present) - (.+) \n # $1: Header row (at least one pipe) - - [ ]{0,'.$less_than_tab.'} # Allowed whitespace. - [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline - - ( # $3: Cells - (?> - [ ]* # Allowed whitespace. - [|] .* \n # Row content. - )* - ) - (?=\n|\Z) # Stop at final double newline. - }xm', - array(&$this, '_doTable_leadingPipe_callback'), $text); - - # - # Find tables without leading pipe. - # - # Header 1 | Header 2 - # -------- | -------- - # Cell 1 | Cell 2 - # Cell 3 | Cell 4 - # - $text = preg_replace_callback(' - { - ^ # Start of a line - [ ]{0,'.$less_than_tab.'} # Allowed whitespace. - (\S.*[|].*) \n # $1: Header row (at least one pipe) - - [ ]{0,'.$less_than_tab.'} # Allowed whitespace. - ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline - - ( # $3: Cells - (?> - .* [|] .* \n # Row content - )* - ) - (?=\n|\Z) # Stop at final double newline. - }xm', - array(&$this, '_DoTable_callback'), $text); - - return $text; - } - protected function _doTable_leadingPipe_callback($matches) { - $head = $matches[1]; - $underline = $matches[2]; - $content = $matches[3]; - - # Remove leading pipe for each row. - $content = preg_replace('/^ *[|]/m', '', $content); - - return $this->_doTable_callback(array($matches[0], $head, $underline, $content)); - } - protected function _doTable_makeAlignAttr($alignname) - { - if (empty($this->table_align_class_tmpl)) - return " align=\"$alignname\""; - - $classname = str_replace('%%', $alignname, $this->table_align_class_tmpl); - return " class=\"$classname\""; - } - protected function _doTable_callback($matches) { - $head = $matches[1]; - $underline = $matches[2]; - $content = $matches[3]; - - # Remove any tailing pipes for each line. - $head = preg_replace('/[|] *$/m', '', $head); - $underline = preg_replace('/[|] *$/m', '', $underline); - $content = preg_replace('/[|] *$/m', '', $content); - - # Reading alignement from header underline. - $separators = preg_split('/ *[|] */', $underline); - foreach ($separators as $n => $s) { - if (preg_match('/^ *-+: *$/', $s)) - $attr[$n] = $this->_doTable_makeAlignAttr('right'); - else if (preg_match('/^ *:-+: *$/', $s)) - $attr[$n] = $this->_doTable_makeAlignAttr('center'); - else if (preg_match('/^ *:-+ *$/', $s)) - $attr[$n] = $this->_doTable_makeAlignAttr('left'); - else - $attr[$n] = ''; - } - - # Parsing span elements, including code spans, character escapes, - # and inline HTML tags, so that pipes inside those gets ignored. - $head = $this->parseSpan($head); - $headers = preg_split('/ *[|] */', $head); - $col_count = count($headers); - $attr = array_pad($attr, $col_count, ''); - - # Write column headers. - $text = "\n"; - $text .= "\n"; - $text .= "\n"; - foreach ($headers as $n => $header) - $text .= " ".$this->runSpanGamut(trim($header))."\n"; - $text .= "\n"; - $text .= "\n"; - - # Split content by row. - $rows = explode("\n", trim($content, "\n")); - - $text .= "\n"; - foreach ($rows as $row) { - # Parsing span elements, including code spans, character escapes, - # and inline HTML tags, so that pipes inside those gets ignored. - $row = $this->parseSpan($row); - - # Split row by cell. - $row_cells = preg_split('/ *[|] */', $row, $col_count); - $row_cells = array_pad($row_cells, $col_count, ''); - - $text .= "\n"; - foreach ($row_cells as $n => $cell) - $text .= " ".$this->runSpanGamut(trim($cell))."\n"; - $text .= "\n"; - } - $text .= "\n"; - $text .= "
    "; - - return $this->hashBlock($text) . "\n"; - } - - - protected function doDefLists($text) { - # - # Form HTML definition lists. - # - $less_than_tab = $this->tab_width - 1; - - # Re-usable pattern to match any entire dl list: - $whole_list_re = '(?> - ( # $1 = whole list - ( # $2 - [ ]{0,'.$less_than_tab.'} - ((?>.*\S.*\n)+) # $3 = defined term - \n? - [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition - ) - (?s:.+?) - ( # $4 - \z - | - \n{2,} - (?=\S) - (?! # Negative lookahead for another term - [ ]{0,'.$less_than_tab.'} - (?: \S.*\n )+? # defined term - \n? - [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition - ) - (?! # Negative lookahead for another definition - [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition - ) - ) - ) - )'; // mx - - $text = preg_replace_callback('{ - (?>\A\n?|(?<=\n\n)) - '.$whole_list_re.' - }mx', - array(&$this, '_doDefLists_callback'), $text); - - return $text; - } - protected function _doDefLists_callback($matches) { - # Re-usable patterns to match list item bullets and number markers: - $list = $matches[1]; - - # Turn double returns into triple returns, so that we can make a - # paragraph for the last item in a list, if necessary: - $result = trim($this->processDefListItems($list)); - $result = "
    \n" . $result . "\n
    "; - return $this->hashBlock($result) . "\n\n"; - } - - - protected function processDefListItems($list_str) { - # - # Process the contents of a single definition list, splitting it - # into individual term and definition list items. - # - $less_than_tab = $this->tab_width - 1; - - # trim trailing blank lines: - $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str); - - # Process definition terms. - $list_str = preg_replace_callback('{ - (?>\A\n?|\n\n+) # leading line - ( # definition terms = $1 - [ ]{0,'.$less_than_tab.'} # leading whitespace - (?!\:[ ]|[ ]) # negative lookahead for a definition - # mark (colon) or more whitespace. - (?> \S.* \n)+? # actual term (not whitespace). - ) - (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed - # with a definition mark. - }xm', - array(&$this, '_processDefListItems_callback_dt'), $list_str); - - # Process actual definitions. - $list_str = preg_replace_callback('{ - \n(\n+)? # leading line = $1 - ( # marker space = $2 - [ ]{0,'.$less_than_tab.'} # whitespace before colon - \:[ ]+ # definition mark (colon) - ) - ((?s:.+?)) # definition text = $3 - (?= \n+ # stop at next definition mark, - (?: # next term or end of text - [ ]{0,'.$less_than_tab.'} \:[ ] | -
    | \z - ) - ) - }xm', - array(&$this, '_processDefListItems_callback_dd'), $list_str); - - return $list_str; - } - protected function _processDefListItems_callback_dt($matches) { - $terms = explode("\n", trim($matches[1])); - $text = ''; - foreach ($terms as $term) { - $term = $this->runSpanGamut(trim($term)); - $text .= "\n
    " . $term . "
    "; - } - return $text . "\n"; - } - protected function _processDefListItems_callback_dd($matches) { - $leading_line = $matches[1]; - $marker_space = $matches[2]; - $def = $matches[3]; - - if ($leading_line || preg_match('/\n{2,}/', $def)) { - # Replace marker with the appropriate whitespace indentation - $def = str_repeat(' ', strlen($marker_space)) . $def; - $def = $this->runBlockGamut($this->outdent($def . "\n\n")); - $def = "\n". $def ."\n"; - } - else { - $def = rtrim($def); - $def = $this->runSpanGamut($this->outdent($def)); - } - - return "\n
    " . $def . "
    \n"; - } - - - protected function doFencedCodeBlocks($text) { - # - # Adding the fenced code block syntax to regular Markdown: - # - # ~~~ - # Code block - # ~~~ - # - $less_than_tab = $this->tab_width; - - $text = preg_replace_callback('{ - (?:\n|\A) - # 1: Opening marker - ( - (?:~{3,}|`{3,}) # 3 or more tildes/backticks. - ) - [ ]* - (?: - \.?([-_:a-zA-Z0-9]+) # 2: standalone class name - | - '.$this->id_class_attr_catch_re.' # 3: Extra attributes - )? - [ ]* \n # Whitespace and newline following marker. - - # 4: Content - ( - (?> - (?!\1 [ ]* \n) # Not a closing marker. - .*\n+ - )+ - ) - - # Closing marker. - \1 [ ]* (?= \n ) - }xm', - array(&$this, '_doFencedCodeBlocks_callback'), $text); - - return $text; - } - protected function _doFencedCodeBlocks_callback($matches) { - $classname =& $matches[2]; - $attrs =& $matches[3]; - $codeblock = $matches[4]; - $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES); - $codeblock = preg_replace_callback('/^\n+/', - array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock); - - if ($classname != "") { - if ($classname{0} == '.') - $classname = substr($classname, 1); - $attr_str = ' class="'.$this->code_class_prefix.$classname.'"'; - } else { - $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs); - } - $pre_attr_str = $this->code_attr_on_pre ? $attr_str : ''; - $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str; - $codeblock = "$codeblock
    "; - - return "\n\n".$this->hashBlock($codeblock)."\n\n"; - } - protected function _doFencedCodeBlocks_newlines($matches) { - return str_repeat("empty_element_suffix", - strlen($matches[0])); - } - - - # - # Redefining emphasis markers so that emphasis by underscore does not - # work in the middle of a word. - # - protected $em_relist = array( - '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags - # - # Strip leading and trailing lines: - $text = preg_replace('/\A\n+|\n+\z/', '', $text); - - $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY); - - # - # Wrap

    tags and unhashify HTML blocks - # - foreach ($grafs as $key => $value) { - $value = trim($this->runSpanGamut($value)); - - # Check if this should be enclosed in a paragraph. - # Clean tag hashes & block tag hashes are left alone. - $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value); - - if ($is_p) { - $value = "

    $value

    "; - } - $grafs[$key] = $value; - } - - # Join grafs in one text, then unhash HTML tags. - $text = implode("\n\n", $grafs); - - # Finish by removing any tag hashes still present in $text. - $text = $this->unhash($text); - - return $text; - } - - - ### Footnotes - - protected function stripFootnotes($text) { - # - # Strips link definitions from text, stores the URLs and titles in - # hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: [^id]: url "optional title" - $text = preg_replace_callback('{ - ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1 - [ ]* - \n? # maybe *one* newline - ( # text = $2 (no blank lines allowed) - (?: - .+ # actual text - | - \n # newlines but - (?!\[\^.+?\]:\s)# negative lookahead for footnote marker. - (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed - # by non-indented content - )* - ) - }xm', - array(&$this, '_stripFootnotes_callback'), - $text); - return $text; - } - protected function _stripFootnotes_callback($matches) { - $note_id = $this->fn_id_prefix . $matches[1]; - $this->footnotes[$note_id] = $this->outdent($matches[2]); - return ''; # String that will replace the block - } - - - protected function doFootnotes($text) { - # - # Replace footnote references in $text [^id] with a special text-token - # which will be replaced by the actual footnote marker in appendFootnotes. - # - if (!$this->in_anchor) { - $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text); - } - return $text; - } - - - protected function appendFootnotes($text) { - # - # Append footnote list to text. - # - $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', - array(&$this, '_appendFootnotes_callback'), $text); - - if (!empty($this->footnotes_ordered)) { - $text .= "\n\n"; - $text .= "
    \n"; - $text .= "empty_element_suffix ."\n"; - $text .= "
      \n\n"; - - $attr = ""; - if ($this->fn_backlink_class != "") { - $class = $this->fn_backlink_class; - $class = $this->encodeAttribute($class); - $attr .= " class=\"$class\""; - } - if ($this->fn_backlink_title != "") { - $title = $this->fn_backlink_title; - $title = $this->encodeAttribute($title); - $attr .= " title=\"$title\""; - } - $num = 0; - - while (!empty($this->footnotes_ordered)) { - $footnote = reset($this->footnotes_ordered); - $note_id = key($this->footnotes_ordered); - unset($this->footnotes_ordered[$note_id]); - $ref_count = $this->footnotes_ref_count[$note_id]; - unset($this->footnotes_ref_count[$note_id]); - unset($this->footnotes[$note_id]); - - $footnote .= "\n"; # Need to append newline before parsing. - $footnote = $this->runBlockGamut("$footnote\n"); - $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}', - array(&$this, '_appendFootnotes_callback'), $footnote); - - $attr = str_replace("%%", ++$num, $attr); - $note_id = $this->encodeAttribute($note_id); - - # Prepare backlink, multiple backlinks if multiple references - $backlink = ""; - for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) { - $backlink .= " "; - } - # Add backlink to last paragraph; create new paragraph if needed. - if (preg_match('{

      $}', $footnote)) { - $footnote = substr($footnote, 0, -4) . " $backlink

      "; - } else { - $footnote .= "\n\n

      $backlink

      "; - } - - $text .= "
    1. \n"; - $text .= $footnote . "\n"; - $text .= "
    2. \n\n"; - } - - $text .= "
    \n"; - $text .= "
    "; - } - return $text; - } - protected function _appendFootnotes_callback($matches) { - $node_id = $this->fn_id_prefix . $matches[1]; - - # Create footnote marker only if it has a corresponding footnote *and* - # the footnote hasn't been used by another marker. - if (isset($this->footnotes[$node_id])) { - $num =& $this->footnotes_numbers[$node_id]; - if (!isset($num)) { - # Transfer footnote content to the ordered list and give it its - # number - $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id]; - $this->footnotes_ref_count[$node_id] = 1; - $num = $this->footnote_counter++; - $ref_count_mark = ''; - } else { - $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1; - } - - $attr = ""; - if ($this->fn_link_class != "") { - $class = $this->fn_link_class; - $class = $this->encodeAttribute($class); - $attr .= " class=\"$class\""; - } - if ($this->fn_link_title != "") { - $title = $this->fn_link_title; - $title = $this->encodeAttribute($title); - $attr .= " title=\"$title\""; - } - - $attr = str_replace("%%", $num, $attr); - $node_id = $this->encodeAttribute($node_id); - - return - "". - "$num". - ""; - } - - return "[^".$matches[1]."]"; - } - - - ### Abbreviations ### - - protected function stripAbbreviations($text) { - # - # Strips abbreviations from text, stores titles in hash references. - # - $less_than_tab = $this->tab_width - 1; - - # Link defs are in the form: [id]*: url "optional title" - $text = preg_replace_callback('{ - ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1 - (.*) # text = $2 (no blank lines allowed) - }xm', - array(&$this, '_stripAbbreviations_callback'), - $text); - return $text; - } - protected function _stripAbbreviations_callback($matches) { - $abbr_word = $matches[1]; - $abbr_desc = $matches[2]; - if ($this->abbr_word_re) - $this->abbr_word_re .= '|'; - $this->abbr_word_re .= preg_quote($abbr_word); - $this->abbr_desciptions[$abbr_word] = trim($abbr_desc); - return ''; # String that will replace the block - } - - - protected function doAbbreviations($text) { - # - # Find defined abbreviations in text and wrap them in elements. - # - if ($this->abbr_word_re) { - // cannot use the /x modifier because abbr_word_re may - // contain significant spaces: - $text = preg_replace_callback('{'. - '(?abbr_word_re.')'. - '(?![\w\x1A])'. - '}', - array(&$this, '_doAbbreviations_callback'), $text); - } - return $text; - } - protected function _doAbbreviations_callback($matches) { - $abbr = $matches[0]; - if (isset($this->abbr_desciptions[$abbr])) { - $desc = $this->abbr_desciptions[$abbr]; - if (empty($desc)) { - return $this->hashPart("$abbr"); - } else { - $desc = $this->encodeAttribute($desc); - return $this->hashPart("$abbr"); - } - } else { - return $matches[0]; - } - } - -} diff --git a/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php b/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php deleted file mode 100644 index e11b1ef97..000000000 --- a/core/lib/michelf/php-markdown/Michelf/MarkdownExtra.inc.php +++ /dev/null @@ -1,11 +0,0 @@ - -# -# Original Markdown -# Copyright (c) 2004-2006 John Gruber -# -# -namespace Michelf; - - -# Just force Michelf/Markdown.php to load. This is needed to load -# the temporary implementation class. See below for details. -\Michelf\Markdown::MARKDOWNLIB_VERSION; - -# -# Markdown Extra Parser Class -# -# Note: Currently the implementation resides in the temporary class -# \Michelf\MarkdownExtra_TmpImpl (in the same file as \Michelf\Markdown). -# This makes it easier to propagate the changes between the three different -# packaging styles of PHP Markdown. Once this issue is resolved, the -# _MarkdownExtra_TmpImpl will disappear and this one will contain the code. -# - -class MarkdownExtra extends \Michelf\_MarkdownExtra_TmpImpl { - - ### Parser Implementation ### - - # Temporarily, the implemenation is in the _MarkdownExtra_TmpImpl class. - # See note above. - -} - diff --git a/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php b/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php deleted file mode 100644 index a023ed4e3..000000000 --- a/core/lib/michelf/php-markdown/Michelf/MarkdownInterface.inc.php +++ /dev/null @@ -1,9 +0,0 @@ - -# -# Original Markdown -# Copyright (c) 2004-2006 John Gruber -# -# -namespace Michelf; - - -# -# Markdown Parser Interface -# - -interface MarkdownInterface { - - # - # Initialize the parser and return the result of its transform method. - # This will work fine for derived classes too. - # - public static function defaultTransform($text); - - # - # Main function. Performs some preprocessing on the input text - # and pass it through the document gamut. - # - public function transform($text); - -} - - -?> \ No newline at end of file diff --git a/core/lib/michelf/php-markdown/Readme.md b/core/lib/michelf/php-markdown/Readme.md deleted file mode 100644 index 25b0cdc0d..000000000 --- a/core/lib/michelf/php-markdown/Readme.md +++ /dev/null @@ -1,271 +0,0 @@ -PHP Markdown -============ - -PHP Markdown Lib 1.4.0 - 29 Nov 2013 - -by Michel Fortin - - -based on Markdown by John Gruber - - - -Introduction ------------- - -This is a library package that includes the PHP Markdown parser and its -sibling PHP Markdown Extra with additional features. - -Markdown is a text-to-HTML conversion tool for web writers. Markdown -allows you to write using an easy-to-read, easy-to-write plain text -format, then convert it to structurally valid XHTML (or HTML). - -"Markdown" is actually two things: a plain text markup syntax, and a -software tool, originally written in Perl, that converts the plain text -markup to HTML. PHP Markdown is a port to PHP of the original Markdown -program by John Gruber. - -* [Full documentation of the Markdown syntax]() - - Daring Fireball (John Gruber) -* [Markdown Extra syntax additions]() - - Michel Fortin - - -Requirement ------------ - -This library package requires PHP 5.3 or later. - -Note: The older plugin/library hybrid package for PHP Markdown and -PHP Markdown Extra is still maintained and will work with PHP 4.0.5 and later. - -Before PHP 5.3.7, pcre.backtrack_limit defaults to 100 000, which is too small -in many situations. You might need to set it to higher values. Later PHP -releases defaults to 1 000 000, which is usually fine. - - -Usage ------ - -This library package is meant to be used with class autoloading. For autoloading -to work, your project needs have setup a PSR-0-compatible autoloader. See the -included Readme.php file for a minimal autoloader setup. (If you cannot use -autoloading, see below.) - -With class autoloading in place, putting the 'Michelf' folder in your -include path should be enough for this to work: - - use \Michelf\Markdown; - $my_html = Markdown::defaultTransform($my_text); - -Markdown Extra syntax is also available the same way: - - use \Michelf\MarkdownExtra; - $my_html = MarkdownExtra::defaultTransform($my_text); - -If you wish to use PHP Markdown with another text filter function -built to parse HTML, you should filter the text *after* the `transform` -function call. This is an example with [PHP SmartyPants][psp]: - - use \Michelf\Markdown, \Michelf\SmartyPants; - $my_html = Markdown::defaultTransform($my_text); - $my_html = SmartyPants::defaultTransform($my_html); - -All these examples are using the static `defaultTransform` static function -found inside the parser class. If you want to customize the parser -configuration, you can also instantiate it directly and change some -configuration variables: - - use \Michelf\MarkdownExtra; - $parser = new MarkdownExtra; - $parser->fn_id_prefix = "post22-"; - $my_html = $parser->transform($my_text); - -To learn more, see the full list of [configuration variables]. - - [configuration variables]: http://michelf.ca/projects/php-markdown/configuration/ - - -### Usage without an autoloader - -If you cannot use class autoloading, you can still use `include` or `require` -to access the parser. To load the `\Michelf\Markdown` parser, do it this way: - - require_once 'Michelf/Markdown.inc.php'; - -Or, if you need the `\Michelf\MarkdownExtra` parser: - - require_once 'Michelf/MarkdownExtra.inc.php'; - -While the plain `.php` files depend on autoloading to work correctly, using the -`.inc.php` files instead will eagerly load the dependencies that would be -loaded on demand if you were using autoloading. - - -Public API and Versioning Policy ---------------------------------- - -Version numbers are of the form *major*.*minor*.*patch*. - -The public API of PHP Markdown consist of the two parser classes `Markdown` -and `MarkdownExtra`, their constructors, the `transform` and `defaultTransform` -functions and their configuration variables. The public API is stable for -a given major version number. It might get additions when the minor version -number increments. - -**Protected members are not considered public API.** This is unconventional -and deserves an explanation. Incrementing the major version number every time -the underlying implementation of something changes is going to give -nonessential version numbers for the vast majority of people who just use the -parser. Protected members are meant to create parser subclasses that behave in -different ways. Very few people create parser subclasses. I don't want to -discourage it by making everything private, but at the same time I can't -guarantee any stable hook between versions if you use protected members. - -**Syntax changes** will increment the minor number for new features, and the -patch number for small corrections. A *new feature* is something that needs a -change in the syntax documentation. Note that since PHP Markdown Lib includes -two parsers, a syntax change for either of them will increment the minor -number. Also note that there is nothing perfectly backward-compatible with the -Markdown syntax: all inputs are always valid, so new features always replace -something that was previously legal, although generally nonsensical to do. - - -Bugs ----- - -To file bug reports please send email to: - - -Please include with your report: (1) the example input; (2) the output you -expected; (3) the output PHP Markdown actually produced. - -If you have a problem where Markdown gives you an empty result, first check -that the backtrack limit is not too low by running `php --info | grep pcre`. -See Installation and Requirement above for details. - - -Development and Testing ------------------------ - -Pull requests for fixing bugs are welcome. Proposed new features are -going meticulously reviewed -- taking into account backward compatibility, -potential side effects, and future extensibility -- before deciding on -acceptance or rejection. - -If you make a pull request that includes changes to the parser please add -tests for what is being changed to [MDTest][] and make a pull request there -too. - - [MDTest]: https://github.com/michelf/mdtest/ - - -Version History ---------------- - -PHP Markdown Lib 1.4.0 (29 Nov 2013) - -* Added support for the `tel:` URL scheme in automatic links. - - - - It gets converted to this (note the `tel:` prefix becomes invisible): - - +1-111-111-1111 - -* Added backtick fenced code blocks to MarkdownExtra, originally from - Github-Flavored Markdown. - -* Added an interface called MarkdownInterface interface implemented by both - the Markdown and MarkdownExtra parsers. You can use the interface if - you want to create a mockup parser object for unit testing. - -* For those of you who cannot use class autoloading, you can now - include `Michelf/Markdown.inc.php` or `Michelf/MarkdownExtra.inc.php` (note - the `.inc.php` extension) to automatically include other files required - by the parser. - - -PHP Markdown Lib 1.3 (11 Apr 2013) - -This is the first release of PHP Markdown Lib. This package requires PHP -version 5.3 or later and is designed to work with PSR-0 autoloading and, -optionally with Composer. Here is a list of the changes since -PHP Markdown Extra 1.2.6: - -* Plugin interface for WordPress and other systems is no longer present in - the Lib package. The classic package is still available if you need it: - - -* Added `public` and `protected` protection attributes, plus a section about - what is "public API" and what isn't in the Readme file. - -* Changed HTML output for footnotes: now instead of adding `rel` and `rev` - attributes, footnotes links have the class name `footnote-ref` and - backlinks `footnote-backref`. - -* Fixed some regular expressions to make PCRE not shout warnings about POSIX - collation classes (dependent on your version of PCRE). - -* Added optional class and id attributes to images and links using the same - syntax as for headers: - - [link](url){#id .class} - ![img](url){#id .class} - - It work too for reference-style links and images. In this case you need - to put those attributes at the reference definition: - - [link][linkref] or [linkref] - ![img][linkref] - - [linkref]: url "optional title" {#id .class} - -* Fixed a PHP notice message triggered when some table column separator - markers are missing on the separator line below column headers. - -* Fixed a small mistake that could cause the parser to retain an invalid - state related to parsing links across multiple runs. This was never - observed (that I know of), but it's still worth fixing. - - -Copyright and License ---------------------- - -PHP Markdown Lib -Copyright (c) 2004-2013 Michel Fortin - -All rights reserved. - -Based on Markdown -Copyright (c) 2003-2005 John Gruber - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -* Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the - distribution. - -* Neither the name "Markdown" nor the names of its contributors may - be used to endorse or promote products derived from this software - without specific prior written permission. - -This software is provided by the copyright holders and contributors "as -is" and any express or implied warranties, including, but not limited -to, the implied warranties of merchantability and fitness for a -particular purpose are disclaimed. In no event shall the copyright owner -or contributors be liable for any direct, indirect, incidental, special, -exemplary, or consequential damages (including, but not limited to, -procurement of substitute goods or services; loss of use, data, or -profits; or business interruption) however caused and on any theory of -liability, whether in contract, strict liability, or tort (including -negligence or otherwise) arising in any way out of the use of this -software, even if advised of the possibility of such damage. diff --git a/core/lib/michelf/php-markdown/Readme.php b/core/lib/michelf/php-markdown/Readme.php deleted file mode 100644 index d007b119f..000000000 --- a/core/lib/michelf/php-markdown/Readme.php +++ /dev/null @@ -1,31 +0,0 @@ - - - - - PHP Markdown Lib - Readme - - - - - diff --git a/core/lib/michelf/php-markdown/composer.json b/core/lib/michelf/php-markdown/composer.json deleted file mode 100644 index 45abc6774..000000000 --- a/core/lib/michelf/php-markdown/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "michelf/php-markdown", - "type": "library", - "description": "PHP Markdown", - "homepage": "http://michelf.ca/projects/php-markdown/", - "keywords": ["markdown"], - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "http://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "http://daringfireball.net/" - } - ], - "require": { - "php": ">=5.3.0" - }, - "autoload": { - "psr-0": { "Michelf": "" } - }, - "extra": { - "branch-alias": { - "dev-lib": "1.4.x-dev" - } - } -} diff --git a/core/lib/pimple/pimple/.gitignore b/core/lib/pimple/pimple/.gitignore deleted file mode 100644 index ce3aa6521..000000000 --- a/core/lib/pimple/pimple/.gitignore +++ /dev/null @@ -1 +0,0 @@ -phpunit.xml diff --git a/core/lib/pimple/pimple/.travis.yml b/core/lib/pimple/pimple/.travis.yml deleted file mode 100644 index 72b4067cd..000000000 --- a/core/lib/pimple/pimple/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: php - -php: - - 5.3 - - 5.4 - - 5.5 diff --git a/core/lib/pimple/pimple/LICENSE b/core/lib/pimple/pimple/LICENSE deleted file mode 100644 index f61e90d96..000000000 --- a/core/lib/pimple/pimple/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2009-2013 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/pimple/pimple/README.rst b/core/lib/pimple/pimple/README.rst deleted file mode 100644 index f685caa62..000000000 --- a/core/lib/pimple/pimple/README.rst +++ /dev/null @@ -1,159 +0,0 @@ -Pimple -====== - -Pimple is a small Dependency Injection Container for PHP 5.3 that consists -of just one file and one class (about 80 lines of code). - -`Download it`_, require it in your code, and you're good to go:: - - require_once '/path/to/Pimple.php'; - -Creating a container is a matter of instating the ``Pimple`` class:: - - $container = new Pimple(); - -As many other dependency injection containers, Pimple is able to manage two -different kind of data: *services* and *parameters*. - -Defining Parameters -------------------- - -Defining a parameter is as simple as using the Pimple instance as an array:: - - // define some parameters - $container['cookie_name'] = 'SESSION_ID'; - $container['session_storage_class'] = 'SessionStorage'; - -Defining Services ------------------ - -A service is an object that does something as part of a larger system. -Examples of services: Database connection, templating engine, mailer. Almost -any object could be a service. - -Services are defined by anonymous functions that return an instance of an -object:: - - // define some services - $container['session_storage'] = function ($c) { - return new $c['session_storage_class']($c['cookie_name']); - }; - - $container['session'] = function ($c) { - return new Session($c['session_storage']); - }; - -Notice that the anonymous function has access to the current container -instance, allowing references to other services or parameters. - -As objects are only created when you get them, the order of the definitions -does not matter, and there is no performance penalty. - -Using the defined services is also very easy:: - - // get the session object - $session = $container['session']; - - // the above call is roughly equivalent to the following code: - // $storage = new SessionStorage('SESSION_ID'); - // $session = new Session($storage); - -Defining Shared Services ------------------------- - -By default, each time you get a service, Pimple returns a new instance of it. -If you want the same instance to be returned for all calls, wrap your -anonymous function with the ``share()`` method:: - - $container['session'] = $container->share(function ($c) { - return new Session($c['session_storage']); - }); - -Protecting Parameters ---------------------- - -Because Pimple sees anonymous functions as service definitions, you need to -wrap anonymous functions with the ``protect()`` method to store them as -parameter:: - - $container['random'] = $container->protect(function () { return rand(); }); - -Modifying services after creation ---------------------------------- - -In some cases you may want to modify a service definition after it has been -defined. You can use the ``extend()`` method to define additional code to -be run on your service just after it is created:: - - $container['mail'] = function ($c) { - return new \Zend_Mail(); - }; - - $container['mail'] = $container->extend('mail', function($mail, $c) { - $mail->setFrom($c['mail.default_from']); - return $mail; - }); - -The first argument is the name of the object, the second is a function that -gets access to the object instance and the container. The return value is -a service definition, so you need to re-assign it on the container. - -If the service you plan to extend is already shared, it's recommended that you -re-wrap your extended service with the ``shared`` method, otherwise your extension -code will be called every time you access the service:: - - $container['twig'] = $container->share(function ($c) { - return new Twig_Environment($c['twig.loader'], $c['twig.options']); - }); - - $container['twig'] = $container->share($container->extend('twig', function ($twig, $c) { - $twig->addExtension(new MyTwigExtension()); - return $twig; - })); - -Fetching the service creation function --------------------------------------- - -When you access an object, Pimple automatically calls the anonymous function -that you defined, which creates the service object for you. If you want to get -raw access to this function, you can use the ``raw()`` method:: - - $container['session'] = $container->share(function ($c) { - return new Session($c['session_storage']); - }); - - $sessionFunction = $container->raw('session'); - -Packaging a Container for reusability -------------------------------------- - -If you use the same libraries over and over, you might want to create reusable -containers. Creating a reusable container is as simple as creating a class -that extends ``Pimple``, and configuring it in the constructor:: - - class SomeContainer extends Pimple - { - public function __construct() - { - $this['parameter'] = 'foo'; - $this['object'] = function () { return stdClass(); }; - } - } - -Using this container from your own is as easy as it can get:: - - $container = new Pimple(); - - // define your project parameters and services - // ... - - // embed the SomeContainer container - $container['embedded'] = $container->share(function () { return new SomeContainer(); }); - - // configure it - $container['embedded']['parameter'] = 'bar'; - - // use it - $container['embedded']['object']->...; - -.. _Download it: https://github.com/fabpot/Pimple diff --git a/core/lib/pimple/pimple/composer.json b/core/lib/pimple/pimple/composer.json deleted file mode 100644 index d95c8c521..000000000 --- a/core/lib/pimple/pimple/composer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "pimple/pimple", - "type": "library", - "description": "Pimple is a simple Dependency Injection Container for PHP 5.3", - "keywords": ["dependency injection", "container"], - "homepage": "http://pimple.sensiolabs.org", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "require": { - "php": ">=5.3.0" - }, - "autoload": { - "psr-0": { "Pimple": "lib/" } - }, - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - } -} \ No newline at end of file diff --git a/core/lib/pimple/pimple/lib/Pimple.php b/core/lib/pimple/pimple/lib/Pimple.php deleted file mode 100644 index b980522d5..000000000 --- a/core/lib/pimple/pimple/lib/Pimple.php +++ /dev/null @@ -1,214 +0,0 @@ -values = $values; - } - - /** - * Sets a parameter or an object. - * - * Objects must be defined as Closures. - * - * Allowing any PHP callable leads to difficult to debug problems - * as function names (strings) are callable (creating a function with - * the same a name as an existing parameter would break your container). - * - * @param string $id The unique identifier for the parameter or object - * @param mixed $value The value of the parameter or a closure to defined an object - */ - public function offsetSet($id, $value) - { - $this->values[$id] = $value; - } - - /** - * Gets a parameter or an object. - * - * @param string $id The unique identifier for the parameter or object - * - * @return mixed The value of the parameter or an object - * - * @throws InvalidArgumentException if the identifier is not defined - */ - public function offsetGet($id) - { - if (!array_key_exists($id, $this->values)) { - throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); - } - - $isFactory = is_object($this->values[$id]) && method_exists($this->values[$id], '__invoke'); - - return $isFactory ? $this->values[$id]($this) : $this->values[$id]; - } - - /** - * Checks if a parameter or an object is set. - * - * @param string $id The unique identifier for the parameter or object - * - * @return Boolean - */ - public function offsetExists($id) - { - return array_key_exists($id, $this->values); - } - - /** - * Unsets a parameter or an object. - * - * @param string $id The unique identifier for the parameter or object - */ - public function offsetUnset($id) - { - unset($this->values[$id]); - } - - /** - * Returns a closure that stores the result of the given service definition - * for uniqueness in the scope of this instance of Pimple. - * - * @param callable $callable A service definition to wrap for uniqueness - * - * @return Closure The wrapped closure - */ - public static function share($callable) - { - if (!is_object($callable) || !method_exists($callable, '__invoke')) { - throw new InvalidArgumentException('Service definition is not a Closure or invokable object.'); - } - - return function ($c) use ($callable) { - static $object; - - if (null === $object) { - $object = $callable($c); - } - - return $object; - }; - } - - /** - * Protects a callable from being interpreted as a service. - * - * This is useful when you want to store a callable as a parameter. - * - * @param callable $callable A callable to protect from being evaluated - * - * @return Closure The protected closure - */ - public static function protect($callable) - { - if (!is_object($callable) || !method_exists($callable, '__invoke')) { - throw new InvalidArgumentException('Callable is not a Closure or invokable object.'); - } - - return function ($c) use ($callable) { - return $callable; - }; - } - - /** - * Gets a parameter or the closure defining an object. - * - * @param string $id The unique identifier for the parameter or object - * - * @return mixed The value of the parameter or the closure defining an object - * - * @throws InvalidArgumentException if the identifier is not defined - */ - public function raw($id) - { - if (!array_key_exists($id, $this->values)) { - throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); - } - - return $this->values[$id]; - } - - /** - * Extends an object definition. - * - * Useful when you want to extend an existing object definition, - * without necessarily loading that object. - * - * @param string $id The unique identifier for the object - * @param callable $callable A service definition to extend the original - * - * @return Closure The wrapped closure - * - * @throws InvalidArgumentException if the identifier is not defined or not a service definition - */ - public function extend($id, $callable) - { - if (!array_key_exists($id, $this->values)) { - throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id)); - } - - if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { - throw new InvalidArgumentException(sprintf('Identifier "%s" does not contain an object definition.', $id)); - } - - if (!is_object($callable) || !method_exists($callable, '__invoke')) { - throw new InvalidArgumentException('Extension service definition is not a Closure or invokable object.'); - } - - $factory = $this->values[$id]; - - return $this->values[$id] = function ($c) use ($callable, $factory) { - return $callable($factory($c), $c); - }; - } - - /** - * Returns all defined value names. - * - * @return array An array of value names - */ - public function keys() - { - return array_keys($this->values); - } -} diff --git a/core/lib/pimple/pimple/phpunit.xml.dist b/core/lib/pimple/pimple/phpunit.xml.dist deleted file mode 100644 index 9de2c19e9..000000000 --- a/core/lib/pimple/pimple/phpunit.xml.dist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - ./tests/Pimple/ - - - diff --git a/core/lib/scan/kss-php/.editorconfig b/core/lib/scan/kss-php/.editorconfig deleted file mode 100644 index 97557e71d..000000000 --- a/core/lib/scan/kss-php/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -; http://EditorConfig.org - -root = true - -[*] -indent_style = space -indent_size = 4 - -charset = utf-8 -end_of_line = lf - -trim_trailing_whitespace = true -insert_final_newline = true diff --git a/core/lib/scan/kss-php/.gitignore b/core/lib/scan/kss-php/.gitignore deleted file mode 100644 index d9b450841..000000000 --- a/core/lib/scan/kss-php/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -build/ -phpunit.xml diff --git a/core/lib/scan/kss-php/ACKNOWLEDGEMENTS b/core/lib/scan/kss-php/ACKNOWLEDGEMENTS deleted file mode 100644 index 1e3a16d32..000000000 --- a/core/lib/scan/kss-php/ACKNOWLEDGEMENTS +++ /dev/null @@ -1,23 +0,0 @@ -Contents of this project are a php conversion of the original ruby version found -at http://github.com/kneath/kss which included the following license: - -Copyright (c) 2011 Tom Preston-Werner, Kyle Neath - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -Software), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/scan/kss-php/CONTRIBUTING.md b/core/lib/scan/kss-php/CONTRIBUTING.md deleted file mode 100644 index e31443909..000000000 --- a/core/lib/scan/kss-php/CONTRIBUTING.md +++ /dev/null @@ -1,41 +0,0 @@ -# Code Modifications - -## Guidelines - -All code modifications must following [PSR-0][], [PSR-1][], and [PSR-2][] as -outlined on the [PHP Framework Interop Group][php-fig]. - -An .editorconfig file is included to help setup your environment if your IDE supports -[EditorConfig][]. - -## Procedure - -* Fork the repository and create a topic branch from where you want to base your work. - * This is usually the master branch. - * To quickly create a topic branch based on master; `git branch - my_contribution master` then checkout the new branch with `git - checkout my_contribution`. Please avoid working directly on the - `master` branch. -* Make commits of logical units. -* Check for unnecessary whitespace with `git diff --check` before committing. -* Make sure your commit messages are in the proper format. - * If your commit messages do not follow this format, please do a - `git rebase -i master` to reword your commit messages. - -```` - Subject Line Describing Your Changes - - The body of your commit message should describe the behavior without your - changes, why this is a problem, and how your changes fix the problem when - applied. -```` - -* Make sure you have added the necessary tests for your changes. -* Run all the tests to assure nothing else was accidentally broken. - -[PSR-0]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md -[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md -[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md -[php-fig]: http://www.php-fig.org -[EditorConfig]: http://editorconfig.org/ - diff --git a/core/lib/scan/kss-php/LICENSE b/core/lib/scan/kss-php/LICENSE deleted file mode 100644 index c588a7033..000000000 --- a/core/lib/scan/kss-php/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013 Scan, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -Software), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/core/lib/scan/kss-php/README.md b/core/lib/scan/kss-php/README.md deleted file mode 100644 index 6be9f7dc3..000000000 --- a/core/lib/scan/kss-php/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# Knyle Style Sheets - -This is a PHP implementation of [Knyle Style Sheets](http://warpspire.com/kss) (KSS). -KSS attempts to provide a methodology for writing maintainable, documented CSS -within a team. Specifically, KSS is a documentation specification and styleguide -format. It is **not** a preprocessor, CSS framework, naming convention, or -specificity guideline. - -* **[The Spec (What KSS is)](https://github.com/kneath/kss/blob/master/SPEC.md)** -* **[Example living styleguide](https://github.com/scaninc/kss-php/tree/master/example)** - -## KSS in a nutshell - -The methodology and ideas behind Knyle Style Sheets are contained in [SPEC.md](https://github.com/kneath/kss/blob/master/SPEC.md) -of the origin [ruby version](https://github.com/kneath/kss) of KSS. At its core, -KSS is a documenting syntax for CSS. - -```css -/* -# Star Button - -A button suitable for giving stars to someone. - -Markup: Button - -:hover - Subtle hover highlight. -.stars--given - A highlight indicating you've already given a star. -.stars--given:hover - Subtle hover highlight on top of stars-given styling. -.stars--disabled - Dims the button to indicate it cannot be used. - -Styleguide 2.1.3. -*/ -a.button.star { - ... -} -a.button.star:hover { - ... -} -a.button.stars--given { - ... -} -a.button.stars--given:hover { - ... -} -a.button.stars--disabled { - ... -} -``` - -## PHP Library - -This repository includes a php library suitable for parsing SASS, SCSS, and CSS -documented with KSS guidelines. To use the library, include it in your project as -a composer dependency (see below). Then, create a parser and explore your KSS. - -```php -getSection('2.1.1'); -// Returns a \Scan\Kss\Section object - -echo $section->getTitle(); -// Echoes "Star Button" - -echo $section->getDescription(); -// echoes "A button suitable for giving stars to someone." - -echo $section->getMarkup(); -// echoes "Button" - -$modifier = current($section->getModifiers()); -// Returns a \Scan\Kss\Modifier object - -echo $modifier->getName(); -// echoes ':hover' - -echo $modifier->getClassName(); -// echoes 'psuedo-class-hover' - -echo $modifier->getDescription(); -// echoes 'Subtle hover highlight' - -echo $modifier->getExampleHtml(); -// echoes Button for the .stars-given modifier -``` - -## Generating styleguides - -The documenting syntax and php library are intended to generate styleguides automatically. -To do this, you'll need to leverage a small javascript library that generates -class styles for pseudo-class styles (`:hover`, `:disabled`, etc). - -* [kss.coffee](https://github.com/scaninc/kss-php/blob/master/lib/Scan/kss.coffee) -* [kss.js](https://github.com/scaninc/kss-php/blob/master/example/public/js/kss.js) (compiled js) - -For an example of how to generate a styleguide, check out the [`example`](https://github.com/scaninc/kss-php/tree/master/example) -php pages. - -## Dependencies - -The PHP version of KSS has dependencies managed by Composer. If you did not install -kss-php using composer, you must install these dependencies manually before using -the library by running the following commands: - -``` -$ composer install -``` - -If you do not yet have Composer, download it following the instructions on -http://getcomposer.org or run the following commands to install it globally on -your system: - -``` -$ curl -s https://getcomposer.org/installer | php -$ sudo mv composer.phar /usr/local/bin/composer -``` - -## Symfony2 Bundle - -If your project uses [symfony2](http://symfony.com/), consider using the [KSS Bundle] -(https://github.com/scaninc/ScanKssBundle) as well. The KSS Bundle uses Twig templates -to make the styleguide block easier to customize and include in your views. diff --git a/core/lib/scan/kss-php/build.xml b/core/lib/scan/kss-php/build.xml deleted file mode 100644 index e59f6fde2..000000000 --- a/core/lib/scan/kss-php/build.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/core/lib/scan/kss-php/composer.json b/core/lib/scan/kss-php/composer.json deleted file mode 100644 index 912ec67e6..000000000 --- a/core/lib/scan/kss-php/composer.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "scan/kss-php", - "description": "A PHP implementation of KSS: a methodology for documenting CSS and generating styleguides", - "keywords": [ - "kss", - "styleguide", - "css documentation" - ], - "license": "MIT", - "require": { - "php": ">=5.3.3", - "symfony/finder": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*" - }, - "authors": [ - { - "name": "Russell Ahlstrom", - "email": "russell.ahlstrom@gmail.com", - "homepage": "http://russell.ahlstromology.com" - } - ], - "autoload": { - "psr-0": { - "Scan\\Kss": "lib/" - } - } -} diff --git a/core/lib/scan/kss-php/composer.lock b/core/lib/scan/kss-php/composer.lock deleted file mode 100644 index 047fdb498..000000000 --- a/core/lib/scan/kss-php/composer.lock +++ /dev/null @@ -1,475 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" - ], - "hash": "52293773fb81531edc12e7533e95963a", - "packages": [ - { - "name": "symfony/finder", - "version": "v2.3.0", - "target-dir": "Symfony/Component/Finder", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "v2.3.0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/v2.3.0", - "reference": "v2.3.0", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.3-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "http://symfony.com", - "time": "2013-06-02 12:05:51" - } - ], - "packages-dev": [ - { - "name": "phpunit/php-code-coverage", - "version": "1.2.11", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "1.2.11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/1.2.11", - "reference": "1.2.11", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.1.1@stable", - "phpunit/php-token-stream": ">=1.1.3@stable" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.0.5" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2013-05-23 18:23:24" - }, - { - "name": "phpunit/php-file-iterator", - "version": "1.3.3", - "source": { - "type": "git", - "url": "git://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "1.3.3" - }, - "dist": { - "type": "zip", - "url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3", - "reference": "1.3.3", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "File/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2012-10-11 04:44:38" - }, - { - "name": "phpunit/php-text-template", - "version": "1.1.4", - "source": { - "type": "git", - "url": "git://github.com/sebastianbergmann/php-text-template.git", - "reference": "1.1.4" - }, - "dist": { - "type": "zip", - "url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.4", - "reference": "1.1.4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "Text/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2012-10-31 11:15:28" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.4", - "source": { - "type": "git", - "url": "git://github.com/sebastianbergmann/php-timer.git", - "reference": "1.0.4" - }, - "dist": { - "type": "zip", - "url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4", - "reference": "1.0.4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "timer" - ], - "time": "2012-10-11 04:45:58" - }, - { - "name": "phpunit/php-token-stream", - "version": "1.1.5", - "source": { - "type": "git", - "url": "git://github.com/sebastianbergmann/php-token-stream.git", - "reference": "1.1.5" - }, - "dist": { - "type": "zip", - "url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5", - "reference": "1.1.5", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "tokenizer" - ], - "time": "2012-10-11 04:47:14" - }, - { - "name": "phpunit/phpunit", - "version": "3.7.21", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3.7.21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3.7.21", - "reference": "3.7.21", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": ">=1.2.1,<1.3.0", - "phpunit/php-file-iterator": ">=1.3.1", - "phpunit/php-text-template": ">=1.1.1", - "phpunit/php-timer": ">=1.0.2,<1.1.0", - "phpunit/phpunit-mock-objects": ">=1.2.0,<1.3.0", - "symfony/yaml": ">=2.0,<3.0" - }, - "require-dev": { - "pear-pear/pear": "1.9.4" - }, - "suggest": { - "ext-json": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "phpunit/php-invoker": ">=1.1.0,<1.2.0" - }, - "bin": [ - "composer/bin/phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2013-05-23 18:54:29" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "1.2.3", - "source": { - "type": "git", - "url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "1.2.3" - }, - "dist": { - "type": "zip", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects/archive/1.2.3.zip", - "reference": "1.2.3", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2013-01-13 10:24:48" - }, - { - "name": "symfony/yaml", - "version": "v2.2.2", - "target-dir": "Symfony/Component/Yaml", - "source": { - "type": "git", - "url": "https://github.com/symfony/Yaml.git", - "reference": "v2.2.2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/v2.2.2", - "reference": "v2.2.2", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Yaml\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "http://symfony.com", - "time": "2013-05-10 18:08:31" - } - ], - "aliases": [ - - ], - "minimum-stability": "stable", - "stability-flags": [ - - ], - "platform": { - "php": ">=5.3.3" - }, - "platform-dev": [ - - ] -} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php b/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php deleted file mode 100644 index 0f8297fc4..000000000 --- a/core/lib/scan/kss-php/lib/Scan/Kss/CommentParser.php +++ /dev/null @@ -1,226 +0,0 @@ -file = $file; - $this->options = $options; - } - - /** - * Returns the parsed comment blocks or if object is not yet parsed, parses - * first and then returns the result - * - * @return array - */ - public function getBlocks() - { - if (!$this->parsed) { - $this->parseBlocks(); - } - return $this->blocks; - } - - /** - * Parses each line of the file looking for single or multi-line comments - * - * @return array - */ - protected function parseBlocks() - { - $this->blocks = array(); - $currentBlock = ''; - // Do we need insideSingleLineBlock? It doesn't seem to be used anywhere - // Original Ruby version of KSS had it but I'm not seeing a purpose to it - $insideSingleLineBlock = false; - $insideMultiLineBlock = false; - - foreach ($this->file as $line) { - $isSingleLineComment = self::isSingleLineComment($line); - $isStartMultiLineComment = self::isStartMultiLineComment($line); - $isEndMultiLineComment = self::isEndMultiLineComment($line); - - if ($isSingleLineComment) { - $parsed = self::parseSingleLineComment($line); - - if ($insideSingleLineBlock) { - $currentBlock .= "\n"; - } else { - $insideSingleLineBlock = true; - } - - $currentBlock .= $parsed; - } - - if ($isStartMultiLineComment || $insideMultiLineBlock) { - $parsed = self::parseMultiLineComment($line); - - if ($insideMultiLineBlock) { - $currentBlock .= "\n"; - } else { - $insideMultiLineBlock = true; - } - - $currentBlock .= $parsed; - } - - if ($isEndMultiLineComment) { - $insideMultiLineBlock = false; - } - - // If we're not in a comment then end the current block and go to - // the next one - if (!$isSingleLineComment && !$insideMultiLineBlock) { - if (!empty($currentBlock)) { - $this->blocks[] = $this->normalize($currentBlock); - $insideSingleLineBlock = false; - $currentBlock = ''; - } - } - } - - $this->parsed = true; - return $this->blocks; - } - - /** - * Makes all the white space consistent among the lines in a comment block. - * That is if the first and second line had 10 spaces but the third line was - * indented to 15 spaces, we'd normalize it so the first and second line have - * no spaces and the third line has 5 spaces. - * - * @param string $block - * - * @return string - */ - protected function normalize($block) - { - // Remove any [whitespace]*'s from the start of each line - $normalizedBlock = preg_replace('-^\s*\*+-m', '', $block); - - $indentSize = null; - $blockLines = explode("\n", $normalizedBlock); - $normalizedLines = array(); - foreach ($blockLines as $line) { - preg_match('/^\s*/', $line, $matches); - $precedingWhitespace = strlen($matches[0]); - if ($indentSize === null) { - $indentSize = $precedingWhitespace; - } - - if ($indentSize <= $precedingWhitespace && $indentSize > 0) { - $line = substr($line, $indentSize); - } - - $normalizedLines[] = $line; - } - - return trim(implode("\n", $normalizedLines)); - } - - /** - * Checks if the comment is a single line comment - * - * @param string $line - * - * @return boolean - */ - public static function isSingleLineComment($line) - { - return (bool) preg_match('-^\s*//-', $line); - } - - /** - * Checks if the line is the start of a multi-line comment - * - * @param string $line - * - * @return boolean - */ - public static function isStartMultiLineComment($line) - { - return (bool) preg_match('-^\s*/\*-', $line); - } - - /** - * Checks if the line is the end of a multi-line comment - * - * @param string $line - * - * @return boolean - */ - public static function isEndMultiLineComment($line) - { - return (bool) preg_match('-.*\*/-', $line); - } - - /** - * Removes the comment markers from a single line comment and trims the line - * - * @param string $line - * - * @return string - */ - public static function parseSingleLineComment($line) - { - return rtrim(preg_replace('-^\s*//-', '', $line)); - } - - /** - * Removes the comment markers from a multi line comment and trims the line - * - * @param string $line - * - * @return string - */ - public static function parseMultiLineComment($line) - { - $parsed = preg_replace('-^\s*/\*+-', '', $line); - $parsed = preg_replace('-\*/-', '', $parsed); - return rtrim($parsed); - } -} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php b/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php deleted file mode 100644 index c6237450d..000000000 --- a/core/lib/scan/kss-php/lib/Scan/Kss/Exception/ExceptionInterface.php +++ /dev/null @@ -1,7 +0,0 @@ -setName($name); - $this->setDescription($description); - } - - /** - * Returns the name of the modifier - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the name of the modifier - * - * @param string $name - */ - public function setName($name) - { - $name = $this->parseExtend($name); - $this->name = $name; - } - - /** - * Returns the description of the modifier - * - * @return string - */ - public function getDescription() - { - return $this->description; - } - - /** - * Sets the description of the modifier - * - * @param string $description - */ - public function setDescription($description) - { - $this->description = $description; - } - - /** - * Returns the markup of the modifier - * - * @return string - */ - public function getMarkup() - { - return $this->markup; - } - - /** - * Sets the markup of the modifier - * - * @param string $markup - */ - public function setMarkup($markup) - { - $this->markup = $markup; - } - - /** - * Checks the name for any extend notations and parses that information - * off and stores it in the $this->extendedClass - * - * @param string $name - * - * @return $name - */ - protected function parseExtend($name) - { - $this->setExtendedClass(null); - - $nameParts = explode('@extend', $name); - $name = trim($nameParts[0]); - if (count($nameParts) > 1) { - $this->setExtendedClass($nameParts[1]); - } - - return $name; - } - - /** - * Returns whether the modifier is applied by extension - * - * @return boolean - */ - public function isExtender() - { - return (bool) $this->getExtendedClass(); - } - - /** - * Returns the extended class - * - * @return string - */ - public function getExtendedClass() - { - return $this->extendedClass; - } - - /** - * Sets the extended class. If the class name is empty, assuming null instead - * and stop further parsing - * - * @param string $class - */ - public function setExtendedClass($class) - { - if (empty($class)) { - $this->extenderClass = null; - return; - } - - $this->extendedClass = trim($class); - } - - /** - * Returns the class name for the extended class - * - * @return string - */ - public function getExtendedClassName() - { - if ($this->getExtendedClass() === null) { - return ''; - } - - $name = str_replace('%', ' ', $this->getExtendedClass()); - $name = str_replace('.', ' ', $name); - $name = str_replace(':', ' pseudo-class-', $name); - return trim($name); - } - - /** - * Returns the class name for the modifier - * - * @return string - */ - public function getClassName() - { - $name = str_replace('.', ' ', $this->name); - $name = str_replace(':', ' pseudo-class-', $name); - return trim($name); - } - - /** - * Returns a string of specified html with inserted class names in the correct - * places for modifiers and extenders. - * - * @param string $html OPTIONAL - * - * @return string $html - */ - public function getExampleHtml($html = null) - { - if ($html === null) { - if ($this->getMarkup() === null) { - return ''; - } - $html = $this->getMarkup(); - } - - if ($this->isExtender()) { - $html = str_replace('$modifierClass', '', $html); - - // Use a positive lookbehind and lookahead to ensure we don't - // replace anything more than just the targeted class name - // for example an element name that is the same as the extended - // class name (e.g. button) - $pattern = sprintf('/(?<="| )%s(?="| )/', $this->getExtendedClassName()); - $html = preg_replace( - $pattern, - $this->getClassName(), - $html - ); - } - - $html = str_replace('$modifierClass', $this->getClassName(), $html); - - return $html; - } -} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php b/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php deleted file mode 100644 index 0204ec2af..000000000 --- a/core/lib/scan/kss-php/lib/Scan/Kss/Parser.php +++ /dev/null @@ -1,196 +0,0 @@ -files()->name('/\.(css|sass|scss|less)$/')->in($paths); - - foreach ($finder as $fileInfo) { - $file = new \splFileObject($fileInfo); - $commentParser = new CommentParser($file); - foreach ($commentParser->getBlocks() as $commentBlock) { - if (self::isKssBlock($commentBlock)) { - $this->addSection($commentBlock, $file); - } - } - } - } - - /** - * Adds a section to the Sections collection - * - * @param string $comment - * @param \splFileObject $file - */ - protected function addSection($comment, \splFileObject $file) - { - $section = new Section($comment, $file); - $this->sections[$section->getReference(true)] = $section; - $this->sectionsSortedByReference = false; - } - - /** - * Returns a Section object matching the requested reference. If reference - * is not found, an empty Section object is returned instead - * - * @param string $reference - * - * @return Section - * - * @throws UnexepectedValueException if reference does not exist - */ - public function getSection($reference) - { - $reference = Section::trimReference($reference); - if (array_key_exists($reference, $this->sections)) { - return $this->sections[$reference]; - } - return false; - } - - /** - * Returns an array of all the sections - * - * @return array - */ - public function getSections() - { - $this->sortSections(); - return $this->sections; - } - - /** - * Returns only the top level sections (i.e. 1.0, 2.0, 3.0, etc.) - * - * @return array - */ - public function getTopLevelSections() - { - $this->sortSectionsByDepth(); - $topLevelSections = array(); - - foreach ($this->sections as $section) { - if ($section->getDepth() != 0) { - break; - } - $topLevelSections[] = $section; - } - - return $topLevelSections; - } - - /** - * Returns an array of children for a specified section reference - * - * @param string $reference - * @param int $levelsDown OPTIONAL - * - * @return array - */ - public function getSectionChildren($reference, $levelsDown = null) - { - $this->sortSections(); - - $sectionKeys = array_keys($this->sections); - $sections = array(); - - $maxDepth = null; - if ($levelsDown !== null) { - $maxDepth = Section::calcDepth($reference) + $levelsDown; - } - - $reference = Section::trimReference($reference); - $reference .= '.'; - - foreach ($sectionKeys as $sectionKey) { - // Only get sections within that level. Do not get the level itself - if (strpos($sectionKey . '.', $reference) === 0 - && $sectionKey . '.' != $reference - ) { - $section = $this->sections[$sectionKey]; - if ($maxDepth !== null && $section->getDepth() > $maxDepth) { - continue; - } - $sections[$sectionKey] = $section; - } - } - - return $sections; - } - - /** - * Method to only sort the sections if they need sorting - * - * @return void - */ - protected function sortSections() - { - if ($this->sectionsSortedByReference) { - return; - } - - uasort($this->sections, '\Scan\Kss\Section::depthScoreSort'); - $this->sectionsSortedByReference = true; - } - - /** - * Method to sort the sections by depth - * - * @return void - */ - protected function sortSectionsByDepth() - { - uasort($this->sections, '\Scan\Kss\Section::depthSort'); - $this->sectionsSortedByReference = false; - } - - /** - * Checks to see if a comment block is a KSS Comment block - * - * @param string $comment - * - * @return boolean - */ - public static function isKssBlock($comment) - { - $commentLines = explode("\n\n", $comment); - $lastLine = end($commentLines); - return (bool) preg_match('/Pattern \S/i', $lastLine); - } -} diff --git a/core/lib/scan/kss-php/lib/Scan/Kss/Section.php b/core/lib/scan/kss-php/lib/Scan/Kss/Section.php deleted file mode 100644 index 7679f9a09..000000000 --- a/core/lib/scan/kss-php/lib/Scan/Kss/Section.php +++ /dev/null @@ -1,518 +0,0 @@ -rawComment = $comment; - $this->file = $file; - } - - /** - * Returns the source filename for where the comment block was located - * - * @return string - */ - public function getFilename() - { - if ($this->file === null) { - return ''; - } - - return $this->file->getFilename(); - } - - /** - * Returns the title of the section - * - * @return string - */ - public function getTitle() - { - $title = ''; - - $titleComment = $this->getTitleComment(); - if (preg_match('/^\s*#+\s*(.+)/', $titleComment, $matches)) { - $title = $matches[1]; - } - - return $title; - } - - /** - * Returns the description for the section - * - * @return string - */ - public function getDescription() - { - $descriptionSections = array(); - - foreach ($this->getCommentSections() as $commentSection) { - // Anything that is not the section comment or modifiers comment - // must be the description comment - if ($commentSection != $this->getReferenceComment() - && $commentSection != $this->getTitleComment() - && $commentSection != $this->getMarkupComment() - && $commentSection != $this->getDeprecatedComment() - && $commentSection != $this->getExperimentalComment() - && $commentSection != $this->getModifiersComment() - ) { - $descriptionSections[] = $commentSection; - } - } - - return implode("\n\n", $descriptionSections); - } - - /** - * Returns the markup defined in the section - * - * @return string - */ - public function getMarkup() - { - if ($this->markup === null) { - if ($markupComment = $this->getMarkupComment()) { - $this->markup = trim(preg_replace('/^\s*Markup:/i', '', $markupComment)); - } - } - - return $this->markup; - } - - /** - * Returns the markup for the normal element (without modifierclass) - * - * @param string $replacement Replacement for $modifierClass variable - * @return void - */ - public function getMarkupNormal($replacement = '') - { - return str_replace('$modifierClass', $replacement, $this->getMarkup()); - } - - /** - * Returns the deprecation notice defined in the section - * - * @return string - */ - public function getDeprecated() - { - if ($this->deprecated === null) { - if ($deprecatedComment = $this->getDeprecatedComment()) { - $this->deprecated = trim(preg_replace('/^\s*Deprecated:/i', '', $deprecatedComment)); - } - } - - return $this->deprecated; - } - - /** - * Returns the experimental notice defined in the section - * - * @return string - */ - public function getExperimental() - { - if ($this->experimental === null) { - if ($experimentalComment = $this->getExperimentalComment()) { - $this->experimental = trim(preg_replace('/^\s*Experimental:/i', '', $experimentalComment)); - } - } - - return $this->experimental; - } - - /** - * Returns the modifiers used in the section - * - * @return array - */ - public function getModifiers() - { - $lastIndent = null; - $modifiers = array(); - - if ($modiferComment = $this->getModifiersComment()) { - $modifierLines = explode("\n", $modiferComment); - foreach ($modifierLines as $line) { - if (empty($line)) { - continue; - } - - preg_match('/^\s*/', $line, $matches); - $indent = strlen($matches[0]); - - if ($lastIndent && $indent > $lastIndent) { - $modifier = end($modifiers); - $modifier->setDescription($modifier->getDescription() + trim($line)); - } else { - $lineParts = explode(' - ', $line); - - $name = trim(array_shift($lineParts)); - - $description = ''; - if (!empty($lineParts)) { - $description = trim(implode(' - ', $lineParts)); - } - $modifier = new Modifier($name, $description); - - // If the CSS has a markup, pass it to the modifier for the example HTML - if ($markup = $this->getMarkup()) { - $modifier->setMarkup($markup); - } - $modifiers[] = $modifier; - } - } - } - - return $modifiers; - } - - /** - * Returns the reference number for the section - * - * @return string - * - * @deprecated Method deprecated in v0.3.1 - */ - public function getSection() - { - return $this->getReference(); - } - - /** - * Returns the reference number for the section - * - * @param boolean $trimmed OPTIONAL - * - * @return string - */ - public function getReference($trimmed = false) - { - if ($this->reference === null) { - $referenceComment = $this->getReferenceComment(); - $referenceComment = preg_replace('/\.$/', '', $referenceComment); - - if (preg_match('/Pattern (\S*)/', $referenceComment, $matches)) { - $this->reference = $matches[1]; - } - } - - return ($trimmed && $this->reference !== null) - ? self::trimReference($this->reference) - : $this->reference; - } - - /** - * Trims off all trailing zeros and periods on a reference - * - * @param string $reference - * - * @return string - */ - public static function trimReference($reference) - { - if (substr($reference, -1) == '.') { - $reference = substr($reference, 0, -1); - } - while (preg_match('/(\.0+)$/', $reference, $matches)) { - $reference = substr($reference, 0, strlen($matches[1]) * -1); - } - return $reference; - } - - /** - * Checks to see if a section belongs to a specified reference - * - * @param string $reference - * - * @return boolean - */ - public function belongsToReference($reference) - { - $reference = self::trimReference($reference); - return strpos($this->getReference() . '.', $reference . '.') === 0; - } - - /** - * Helper method for calculating the depth of the instantiated section - * - * @return int - */ - public function getDepth() - { - return self::calcDepth($this->getReference()); - } - - /** - * Calculates and returns the depth of a section reference - * - * @param string $reference - * - * @return int - */ - public static function calcDepth($reference) - { - $reference = self::trimReference($reference); - return substr_count($reference, '.'); - } - - /** - * Helper method for calculating the score of the instantiated section - * - * @return int - */ - public function getDepthScore() - { - return self::calcDepthScore($this->getReference()); - } - /** - * Calculates and returns the depth score for the section. Useful for sorting - * sections correctly by their section reference numbers - * - * @return int - */ - public static function calcDepthScore($reference) - { - $reference = self::trimReference($reference); - $sectionParts = explode('.', $reference); - $score = 0; - foreach ($sectionParts as $level => $part) { - $score += $part * (1 / pow(10, $level)); - } - return $score; - } - - /** - * Function to help sort sections by depth and then depth score - * - * @param Section $a - * @param Section $b - * - * @return int - */ - public static function depthSort(Section $a, Section $b) - { - if ($a->getDepth() == $b->getDepth()) { - return self::depthScoreSort($a, $b); - } - return $a->getDepth() > $b->getDepth(); - } - - /** - * Function to help sort sections by their depth score - * - * @param Section $a - * @param Section $b - * - * @return int - */ - public static function depthScoreSort(Section $a, Section $b) - { - return $a->getDepthScore() > $b->getDepthScore(); - } - - /** - * Returns the comment block used when creating the section as an array of - * paragraphs within the comment block - * - * @return array - */ - protected function getCommentSections() - { - if (empty($this->commentSections) && $this->rawComment) { - $this->commentSections = explode("\n\n", $this->rawComment); - } - - return $this->commentSections; - } - - /** - * Gets the title part of the KSS Comment Block - * - * @return string - */ - protected function getTitleComment() - { - $titleComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Identify the title by the # markdown header syntax - if (preg_match('/^\s*#/i', $commentSection)) { - $titleComment = $commentSection; - break; - } - } - - return $titleComment; - } - - /** - * Returns the part of the KSS Comment Block that contains the markup - * - * @return string - */ - protected function getMarkupComment() - { - $markupComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Identify the markup comment by the Markup: marker - if (preg_match('/^\s*Markup:/i', $commentSection)) { - $markupComment = $commentSection; - break; - } - } - - return $markupComment; - } - - /** - * Returns the part of the KSS Comment Block that contains the deprecated - * notice - * - * @return string - */ - protected function getDeprecatedComment() - { - $deprecatedComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Identify the deprecation notice by the Deprecated: marker - if (preg_match('/^\s*Deprecated:/i', $commentSection)) { - $deprecatedComment = $commentSection; - break; - } - } - - return $deprecatedComment; - } - - /** - * Returns the part of the KSS Comment Block that contains the experimental - * notice - * - * @return string - */ - protected function getExperimentalComment() - { - $experimentalComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Identify the experimental notice by the Experimental: marker - if (preg_match('/^\s*Experimental:/i', $commentSection)) { - $experimentalComment = $commentSection; - break; - } - } - - return $experimentalComment; - } - - /** - * Gets the part of the KSS Comment Block that contains the section reference - * - * @return string - */ - protected function getReferenceComment() - { - $referenceComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Identify it by the Styleguide 1.2.3. pattern - if (preg_match('/Pattern \S/i', $commentSection)) { - $referenceComment = $commentSection; - break; - } - } - - return $referenceComment; - } - - /** - * Returns the part of the KSS Comment Block that contains the modifiers - * - * @return string - */ - protected function getModifiersComment() - { - $modifiersComment = null; - - foreach ($this->getCommentSections() as $commentSection) { - // Assume that the modifiers section starts with either a class or a - // pseudo class - if (preg_match('/^\s*(?:\.|:)/', $commentSection)) { - $modifiersComment = $commentSection; - break; - } - } - - return $modifiersComment; - } -} diff --git a/core/lib/scan/kss-php/lib/Scan/kss.coffee b/core/lib/scan/kss-php/lib/Scan/kss.coffee deleted file mode 100644 index 62d66e441..000000000 --- a/core/lib/scan/kss-php/lib/Scan/kss.coffee +++ /dev/null @@ -1,41 +0,0 @@ -# This class scans your stylesheets for pseudo classes, then inserts a new CSS -# rule with the same properties, but named 'psuedo-class-{{name}}'. -# -# Supported pseudo classes: hover, disabled, active, visited, focus. -# -# Example: -# -# a:hover{ color:blue; } -# => a.pseudo-class-hover{ color:blue; } -class KssStateGenerator - constructor: -> - pseudos = /(\:hover|\:disabled|\:active|\:visited|\:focus|\:target)/g - - try - for stylesheet in document.styleSheets - if stylesheet.href.indexOf(document.domain) >= 0 - idxs = [] - for rule, idx in stylesheet.cssRules - if (rule.type == CSSRule.STYLE_RULE) && pseudos.test(rule.selectorText) - replaceRule = (matched, stuff) -> - return matched.replace(/\:/g, '.pseudo-class-') - @insertRule(rule.cssText.replace(pseudos, replaceRule)) - pseudos.lastIndex = 0 - - # Takes a given style and attaches it to the current page. - # - # rule - A CSS rule String (ex: ".test{ display:none; }"). - # - # Returns nothing. - insertRule: (rule) -> - headEl = document.getElementsByTagName('head')[0] - styleEl = document.createElement('style') - styleEl.type = 'text/css' - if styleEl.styleSheet - styleEl.styleSheet.cssText = rule - else - styleEl.appendChild(document.createTextNode(rule)) - - headEl.appendChild(styleEl) - -new KssStateGenerator diff --git a/core/lib/scan/kss-php/phpunit.xml.dist b/core/lib/scan/kss-php/phpunit.xml.dist deleted file mode 100644 index ed1ba195e..000000000 --- a/core/lib/scan/kss-php/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - test - - - - - - - - - - - - lib - - - diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md deleted file mode 100644 index 536c5ac72..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/CHANGELOG.md +++ /dev/null @@ -1,16 +0,0 @@ -CHANGELOG -========= - -2.1.0 ------ - - * added TraceableEventDispatcherInterface - * added ContainerAwareEventDispatcher - * added a reference to the EventDispatcher on the Event - * added a reference to the Event name on the event - * added fluid interface to the dispatch() method which now returns the Event - object - * added GenericEvent event class - * added the possibility for subscribers to subscribe several times for the - same event - * added ImmutableEventDispatcher diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php deleted file mode 100644 index e97d427ea..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php +++ /dev/null @@ -1,202 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -use Symfony\Component\DependencyInjection\ContainerInterface; - -/** - * Lazily loads listeners and subscribers from the dependency injection - * container - * - * @author Fabien Potencier - * @author Bernhard Schussek - * @author Jordan Alliot - */ -class ContainerAwareEventDispatcher extends EventDispatcher -{ - /** - * The container from where services are loaded - * @var ContainerInterface - */ - private $container; - - /** - * The service IDs of the event listeners and subscribers - * @var array - */ - private $listenerIds = array(); - - /** - * The services registered as listeners - * @var array - */ - private $listeners = array(); - - /** - * Constructor. - * - * @param ContainerInterface $container A ContainerInterface instance - */ - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - /** - * Adds a service as event listener - * - * @param string $eventName Event for which the listener is added - * @param array $callback The service ID of the listener service & the method - * name that has to be called - * @param int $priority The higher this value, the earlier an event listener - * will be triggered in the chain. - * Defaults to 0. - * - * @throws \InvalidArgumentException - */ - public function addListenerService($eventName, $callback, $priority = 0) - { - if (!is_array($callback) || 2 !== count($callback)) { - throw new \InvalidArgumentException('Expected an array("service", "method") argument'); - } - - $this->listenerIds[$eventName][] = array($callback[0], $callback[1], $priority); - } - - public function removeListener($eventName, $listener) - { - $this->lazyLoad($eventName); - - if (isset($this->listeners[$eventName])) { - foreach ($this->listeners[$eventName] as $key => $l) { - foreach ($this->listenerIds[$eventName] as $i => $args) { - list($serviceId, $method, $priority) = $args; - if ($key === $serviceId.'.'.$method) { - if ($listener === array($l, $method)) { - unset($this->listeners[$eventName][$key]); - if (empty($this->listeners[$eventName])) { - unset($this->listeners[$eventName]); - } - unset($this->listenerIds[$eventName][$i]); - if (empty($this->listenerIds[$eventName])) { - unset($this->listenerIds[$eventName]); - } - } - } - } - } - } - - parent::removeListener($eventName, $listener); - } - - /** - * @see EventDispatcherInterface::hasListeners - */ - public function hasListeners($eventName = null) - { - if (null === $eventName) { - return (bool) count($this->listenerIds) || (bool) count($this->listeners); - } - - if (isset($this->listenerIds[$eventName])) { - return true; - } - - return parent::hasListeners($eventName); - } - - /** - * @see EventDispatcherInterface::getListeners - */ - public function getListeners($eventName = null) - { - if (null === $eventName) { - foreach (array_keys($this->listenerIds) as $serviceEventName) { - $this->lazyLoad($serviceEventName); - } - } else { - $this->lazyLoad($eventName); - } - - return parent::getListeners($eventName); - } - - /** - * Adds a service as event subscriber - * - * @param string $serviceId The service ID of the subscriber service - * @param string $class The service's class name (which must implement EventSubscriberInterface) - */ - public function addSubscriberService($serviceId, $class) - { - foreach ($class::getSubscribedEvents() as $eventName => $params) { - if (is_string($params)) { - $this->listenerIds[$eventName][] = array($serviceId, $params, 0); - } elseif (is_string($params[0])) { - $this->listenerIds[$eventName][] = array($serviceId, $params[0], isset($params[1]) ? $params[1] : 0); - } else { - foreach ($params as $listener) { - $this->listenerIds[$eventName][] = array($serviceId, $listener[0], isset($listener[1]) ? $listener[1] : 0); - } - } - } - } - - /** - * {@inheritdoc} - * - * Lazily loads listeners for this event from the dependency injection - * container. - * - * @throws \InvalidArgumentException if the service is not defined - */ - public function dispatch($eventName, Event $event = null) - { - $this->lazyLoad($eventName); - - return parent::dispatch($eventName, $event); - } - - public function getContainer() - { - return $this->container; - } - - /** - * Lazily loads listeners for this event from the dependency injection - * container. - * - * @param string $eventName The name of the event to dispatch. The name of - * the event is the name of the method that is - * invoked on listeners. - */ - protected function lazyLoad($eventName) - { - if (isset($this->listenerIds[$eventName])) { - foreach ($this->listenerIds[$eventName] as $args) { - list($serviceId, $method, $priority) = $args; - $listener = $this->container->get($serviceId); - - $key = $serviceId.'.'.$method; - if (!isset($this->listeners[$eventName][$key])) { - $this->addListener($eventName, array($listener, $method), $priority); - } elseif ($listener !== $this->listeners[$eventName][$key]) { - parent::removeListener($eventName, array($this->listeners[$eventName][$key], $method)); - $this->addListener($eventName, array($listener, $method), $priority); - } - - $this->listeners[$eventName][$key] = $listener; - } - } - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php deleted file mode 100644 index a67a97901..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcherInterface.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Debug; - -/** - * @author Fabien Potencier - */ -interface TraceableEventDispatcherInterface -{ - /** - * Gets the called listeners. - * - * @return array An array of called listeners - */ - public function getCalledListeners(); - - /** - * Gets the not called listeners. - * - * @return array An array of not called listeners - */ - public function getNotCalledListeners(); -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php deleted file mode 100644 index bf792a257..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Event.php +++ /dev/null @@ -1,129 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * Event is the base class for classes containing event data. - * - * This class contains no event data. It is used by events that do not pass - * state information to an event handler when an event is raised. - * - * You can call the method stopPropagation() to abort the execution of - * further listeners in your event listener. - * - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author Bernhard Schussek - * - * @api - */ -class Event -{ - /** - * @var bool Whether no further event listeners should be triggered - */ - private $propagationStopped = false; - - /** - * @var EventDispatcher Dispatcher that dispatched this event - */ - private $dispatcher; - - /** - * @var string This event's name - */ - private $name; - - /** - * Returns whether further event listeners should be triggered. - * - * @see Event::stopPropagation - * @return bool Whether propagation was already stopped for this event. - * - * @api - */ - public function isPropagationStopped() - { - return $this->propagationStopped; - } - - /** - * Stops the propagation of the event to further event listeners. - * - * If multiple event listeners are connected to the same event, no - * further event listener will be triggered once any trigger calls - * stopPropagation(). - * - * @api - */ - public function stopPropagation() - { - $this->propagationStopped = true; - } - - /** - * Stores the EventDispatcher that dispatches this Event - * - * @param EventDispatcherInterface $dispatcher - * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. - * - * @api - */ - public function setDispatcher(EventDispatcherInterface $dispatcher) - { - $this->dispatcher = $dispatcher; - } - - /** - * Returns the EventDispatcher that dispatches this Event - * - * @return EventDispatcherInterface - * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. - * - * @api - */ - public function getDispatcher() - { - return $this->dispatcher; - } - - /** - * Gets the event's name. - * - * @return string - * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. - * - * @api - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the event's name property. - * - * @param string $name The event name. - * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. - * - * @api - */ - public function setName($name) - { - $this->name = $name; - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php deleted file mode 100644 index ab140a669..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcher.php +++ /dev/null @@ -1,185 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * The EventDispatcherInterface is the central point of Symfony's event listener system. - * - * Listeners are registered on the manager and events are dispatched through the - * manager. - * - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author Bernhard Schussek - * @author Fabien Potencier - * @author Jordi Boggiano - * @author Jordan Alliot - * - * @api - */ -class EventDispatcher implements EventDispatcherInterface -{ - private $listeners = array(); - private $sorted = array(); - - /** - * @see EventDispatcherInterface::dispatch - * - * @api - */ - public function dispatch($eventName, Event $event = null) - { - if (null === $event) { - $event = new Event(); - } - - $event->setDispatcher($this); - $event->setName($eventName); - - if (!isset($this->listeners[$eventName])) { - return $event; - } - - $this->doDispatch($this->getListeners($eventName), $eventName, $event); - - return $event; - } - - /** - * @see EventDispatcherInterface::getListeners - */ - public function getListeners($eventName = null) - { - if (null !== $eventName) { - if (!isset($this->sorted[$eventName])) { - $this->sortListeners($eventName); - } - - return $this->sorted[$eventName]; - } - - foreach (array_keys($this->listeners) as $eventName) { - if (!isset($this->sorted[$eventName])) { - $this->sortListeners($eventName); - } - } - - return $this->sorted; - } - - /** - * @see EventDispatcherInterface::hasListeners - */ - public function hasListeners($eventName = null) - { - return (bool) count($this->getListeners($eventName)); - } - - /** - * @see EventDispatcherInterface::addListener - * - * @api - */ - public function addListener($eventName, $listener, $priority = 0) - { - $this->listeners[$eventName][$priority][] = $listener; - unset($this->sorted[$eventName]); - } - - /** - * @see EventDispatcherInterface::removeListener - */ - public function removeListener($eventName, $listener) - { - if (!isset($this->listeners[$eventName])) { - return; - } - - foreach ($this->listeners[$eventName] as $priority => $listeners) { - if (false !== ($key = array_search($listener, $listeners, true))) { - unset($this->listeners[$eventName][$priority][$key], $this->sorted[$eventName]); - } - } - } - - /** - * @see EventDispatcherInterface::addSubscriber - * - * @api - */ - public function addSubscriber(EventSubscriberInterface $subscriber) - { - foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { - if (is_string($params)) { - $this->addListener($eventName, array($subscriber, $params)); - } elseif (is_string($params[0])) { - $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); - } else { - foreach ($params as $listener) { - $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); - } - } - } - } - - /** - * @see EventDispatcherInterface::removeSubscriber - */ - public function removeSubscriber(EventSubscriberInterface $subscriber) - { - foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { - if (is_array($params) && is_array($params[0])) { - foreach ($params as $listener) { - $this->removeListener($eventName, array($subscriber, $listener[0])); - } - } else { - $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0])); - } - } - } - - /** - * Triggers the listeners of an event. - * - * This method can be overridden to add functionality that is executed - * for each listener. - * - * @param callable[] $listeners The event listeners. - * @param string $eventName The name of the event to dispatch. - * @param Event $event The event object to pass to the event handlers/listeners. - */ - protected function doDispatch($listeners, $eventName, Event $event) - { - foreach ($listeners as $listener) { - call_user_func($listener, $event, $eventName, $this); - if ($event->isPropagationStopped()) { - break; - } - } - } - - /** - * Sorts the internal list of listeners for the given event by priority. - * - * @param string $eventName The name of the event. - */ - private function sortListeners($eventName) - { - $this->sorted[$eventName] = array(); - - if (isset($this->listeners[$eventName])) { - krsort($this->listeners[$eventName]); - $this->sorted[$eventName] = call_user_func_array('array_merge', $this->listeners[$eventName]); - } - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php deleted file mode 100644 index 3fdbfd882..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventDispatcherInterface.php +++ /dev/null @@ -1,96 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * The EventDispatcherInterface is the central point of Symfony's event listener system. - * Listeners are registered on the manager and events are dispatched through the - * manager. - * - * @author Bernhard Schussek - * - * @api - */ -interface EventDispatcherInterface -{ - /** - * Dispatches an event to all registered listeners. - * - * @param string $eventName The name of the event to dispatch. The name of - * the event is the name of the method that is - * invoked on listeners. - * @param Event $event The event to pass to the event handlers/listeners. - * If not supplied, an empty Event instance is created. - * - * @return Event - * - * @api - */ - public function dispatch($eventName, Event $event = null); - - /** - * Adds an event listener that listens on the specified events. - * - * @param string $eventName The event to listen on - * @param callable $listener The listener - * @param int $priority The higher this value, the earlier an event - * listener will be triggered in the chain (defaults to 0) - * - * @api - */ - public function addListener($eventName, $listener, $priority = 0); - - /** - * Adds an event subscriber. - * - * The subscriber is asked for all the events he is - * interested in and added as a listener for these events. - * - * @param EventSubscriberInterface $subscriber The subscriber. - * - * @api - */ - public function addSubscriber(EventSubscriberInterface $subscriber); - - /** - * Removes an event listener from the specified events. - * - * @param string|array $eventName The event(s) to remove a listener from - * @param callable $listener The listener to remove - */ - public function removeListener($eventName, $listener); - - /** - * Removes an event subscriber. - * - * @param EventSubscriberInterface $subscriber The subscriber - */ - public function removeSubscriber(EventSubscriberInterface $subscriber); - - /** - * Gets the listeners of a specific event or all listeners. - * - * @param string $eventName The name of the event - * - * @return array The event listeners for the specified event, or all event listeners by event name - */ - public function getListeners($eventName = null); - - /** - * Checks whether an event has any registered listeners. - * - * @param string $eventName The name of the event - * - * @return bool true if the specified event has any listeners, false otherwise - */ - public function hasListeners($eventName = null); -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php deleted file mode 100644 index 080f892fd..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/EventSubscriberInterface.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * An EventSubscriber knows himself what events he is interested in. - * If an EventSubscriber is added to an EventDispatcherInterface, the manager invokes - * {@link getSubscribedEvents} and registers the subscriber as a listener for all - * returned events. - * - * @author Guilherme Blanco - * @author Jonathan Wage - * @author Roman Borschel - * @author Bernhard Schussek - * - * @api - */ -interface EventSubscriberInterface -{ - /** - * Returns an array of event names this subscriber wants to listen to. - * - * The array keys are event names and the value can be: - * - * * The method name to call (priority defaults to 0) - * * An array composed of the method name to call and the priority - * * An array of arrays composed of the method names to call and respective - * priorities, or 0 if unset - * - * For instance: - * - * * array('eventName' => 'methodName') - * * array('eventName' => array('methodName', $priority)) - * * array('eventName' => array(array('methodName1', $priority), array('methodName2')) - * - * @return array The event names to listen to - * - * @api - */ - public static function getSubscribedEvents(); -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php deleted file mode 100644 index 1e8c44a67..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/GenericEvent.php +++ /dev/null @@ -1,186 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * Event encapsulation class. - * - * Encapsulates events thus decoupling the observer from the subject they encapsulate. - * - * @author Drak - */ -class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate -{ - /** - * Event subject. - * - * @var mixed usually object or callable - */ - protected $subject; - - /** - * Array of arguments. - * - * @var array - */ - protected $arguments; - - /** - * Encapsulate an event with $subject and $args. - * - * @param mixed $subject The subject of the event, usually an object. - * @param array $arguments Arguments to store in the event. - */ - public function __construct($subject = null, array $arguments = array()) - { - $this->subject = $subject; - $this->arguments = $arguments; - } - - /** - * Getter for subject property. - * - * @return mixed $subject The observer subject. - */ - public function getSubject() - { - return $this->subject; - } - - /** - * Get argument by key. - * - * @param string $key Key. - * - * @throws \InvalidArgumentException If key is not found. - * - * @return mixed Contents of array key. - */ - public function getArgument($key) - { - if ($this->hasArgument($key)) { - return $this->arguments[$key]; - } - - throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); - } - - /** - * Add argument to event. - * - * @param string $key Argument name. - * @param mixed $value Value. - * - * @return GenericEvent - */ - public function setArgument($key, $value) - { - $this->arguments[$key] = $value; - - return $this; - } - - /** - * Getter for all arguments. - * - * @return array - */ - public function getArguments() - { - return $this->arguments; - } - - /** - * Set args property. - * - * @param array $args Arguments. - * - * @return GenericEvent - */ - public function setArguments(array $args = array()) - { - $this->arguments = $args; - - return $this; - } - - /** - * Has argument. - * - * @param string $key Key of arguments array. - * - * @return bool - */ - public function hasArgument($key) - { - return array_key_exists($key, $this->arguments); - } - - /** - * ArrayAccess for argument getter. - * - * @param string $key Array key. - * - * @throws \InvalidArgumentException If key does not exist in $this->args. - * - * @return mixed - */ - public function offsetGet($key) - { - return $this->getArgument($key); - } - - /** - * ArrayAccess for argument setter. - * - * @param string $key Array key to set. - * @param mixed $value Value. - */ - public function offsetSet($key, $value) - { - $this->setArgument($key, $value); - } - - /** - * ArrayAccess for unset argument. - * - * @param string $key Array key. - */ - public function offsetUnset($key) - { - if ($this->hasArgument($key)) { - unset($this->arguments[$key]); - } - } - - /** - * ArrayAccess has argument. - * - * @param string $key Array key. - * - * @return bool - */ - public function offsetExists($key) - { - return $this->hasArgument($key); - } - - /** - * IteratorAggregate for iterating over the object like an array - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new \ArrayIterator($this->arguments); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php deleted file mode 100644 index b70b81a8b..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/ImmutableEventDispatcher.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher; - -/** - * A read-only proxy for an event dispatcher. - * - * @author Bernhard Schussek - */ -class ImmutableEventDispatcher implements EventDispatcherInterface -{ - /** - * The proxied dispatcher. - * @var EventDispatcherInterface - */ - private $dispatcher; - - /** - * Creates an unmodifiable proxy for an event dispatcher. - * - * @param EventDispatcherInterface $dispatcher The proxied event dispatcher. - */ - public function __construct(EventDispatcherInterface $dispatcher) - { - $this->dispatcher = $dispatcher; - } - - /** - * {@inheritdoc} - */ - public function dispatch($eventName, Event $event = null) - { - return $this->dispatcher->dispatch($eventName, $event); - } - - /** - * {@inheritdoc} - */ - public function addListener($eventName, $listener, $priority = 0) - { - throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); - } - - /** - * {@inheritdoc} - */ - public function addSubscriber(EventSubscriberInterface $subscriber) - { - throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); - } - - /** - * {@inheritdoc} - */ - public function removeListener($eventName, $listener) - { - throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); - } - - /** - * {@inheritdoc} - */ - public function removeSubscriber(EventSubscriberInterface $subscriber) - { - throw new \BadMethodCallException('Unmodifiable event dispatchers must not be modified.'); - } - - /** - * {@inheritdoc} - */ - public function getListeners($eventName = null) - { - return $this->dispatcher->getListeners($eventName); - } - - /** - * {@inheritdoc} - */ - public function hasListeners($eventName = null) - { - return $this->dispatcher->hasListeners($eventName); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE deleted file mode 100644 index 0b3292cf9..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2014 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md deleted file mode 100644 index 22bf74fdc..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/README.md +++ /dev/null @@ -1,25 +0,0 @@ -EventDispatcher Component -========================= - -The Symfony2 EventDispatcher component implements the Mediator pattern in a -simple and effective way to make your projects truly extensible. - - use Symfony\Component\EventDispatcher\EventDispatcher; - use Symfony\Component\EventDispatcher\Event; - - $dispatcher = new EventDispatcher(); - - $dispatcher->addListener('event_name', function (Event $event) { - // ... - }); - - $dispatcher->dispatch('event_name'); - -Resources ---------- - -You can run the unit tests with the following command: - - $ cd path/to/Symfony/Component/EventDispatcher/ - $ composer.phar install - $ phpunit diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php deleted file mode 100644 index fb3b4caa2..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ContainerAwareEventDispatcherTest.php +++ /dev/null @@ -1,244 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\DependencyInjection\Scope; -use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - public function testAddAListenerService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testAddASubscriberService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\SubscriberService'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.subscriber', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addSubscriberService('service.subscriber', 'Symfony\Component\EventDispatcher\Tests\SubscriberService'); - - $dispatcher->dispatch('onEvent', $event); - } - - public function testPreventDuplicateListenerService() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 5); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent'), 10); - - $dispatcher->dispatch('onEvent', $event); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testTriggerAListenerServiceOutOfScope() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $scope = new Scope('scope'); - $container = new Container(); - $container->addScope($scope); - $container->enterScope('scope'); - - $container->set('service.listener', $service, 'scope'); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $container->leaveScope('scope'); - $dispatcher->dispatch('onEvent'); - } - - public function testReEnteringAScope() - { - $event = new Event(); - - $service1 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service1 - ->expects($this->exactly(2)) - ->method('onEvent') - ->with($event) - ; - - $scope = new Scope('scope'); - $container = new Container(); - $container->addScope($scope); - $container->enterScope('scope'); - - $container->set('service.listener', $service1, 'scope'); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - $dispatcher->dispatch('onEvent', $event); - - $service2 = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $service2 - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $container->enterScope('scope'); - $container->set('service.listener', $service2, 'scope'); - - $dispatcher->dispatch('onEvent', $event); - - $container->leaveScope('scope'); - - $dispatcher->dispatch('onEvent'); - } - - public function testHasListenersOnLazyLoad() - { - $event = new Event(); - - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $event->setDispatcher($dispatcher); - $event->setName('onEvent'); - - $service - ->expects($this->once()) - ->method('onEvent') - ->with($event) - ; - - $this->assertTrue($dispatcher->hasListeners()); - - if ($dispatcher->hasListeners('onEvent')) { - $dispatcher->dispatch('onEvent'); - } - } - - public function testGetListenersOnLazyLoad() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $listeners = $dispatcher->getListeners(); - - $this->assertTrue(isset($listeners['onEvent'])); - - $this->assertCount(1, $dispatcher->getListeners('onEvent')); - } - - public function testRemoveAfterDispatch() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->dispatch('onEvent', new Event()); - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } - - public function testRemoveBeforeDispatch() - { - $service = $this->getMock('Symfony\Component\EventDispatcher\Tests\Service'); - - $container = new Container(); - $container->set('service.listener', $service); - - $dispatcher = new ContainerAwareEventDispatcher($container); - $dispatcher->addListenerService('onEvent', array('service.listener', 'onEvent')); - - $dispatcher->removeListener('onEvent', array($container->get('service.listener'), 'onEvent')); - $this->assertFalse($dispatcher->hasListeners('onEvent')); - } -} - -class Service -{ - public function onEvent(Event $e) - { - } -} - -class SubscriberService implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'onEvent' => 'onEvent', - 'onEvent' => array('onEvent', 10), - 'onEvent' => array('onEvent'), - ); - } - - public function onEvent(Event $e) - { - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php deleted file mode 100644 index 1e3282f8d..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventDispatcherTest.php +++ /dev/null @@ -1,346 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class EventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - /* Some pseudo events */ - const preFoo = 'pre.foo'; - const postFoo = 'post.foo'; - const preBar = 'pre.bar'; - const postBar = 'post.bar'; - - /** - * @var EventDispatcher - */ - private $dispatcher; - - private $listener; - - protected function setUp() - { - $this->dispatcher = new EventDispatcher(); - $this->listener = new TestEventListener(); - } - - protected function tearDown() - { - $this->dispatcher = null; - $this->listener = null; - } - - public function testInitialState() - { - $this->assertEquals(array(), $this->dispatcher->getListeners()); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddListener() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); - $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); - $this->assertCount(2, $this->dispatcher->getListeners()); - } - - public function testGetListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener1->name = '1'; - $listener2->name = '2'; - $listener3->name = '3'; - - $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); - $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); - $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); - - $expected = array( - array($listener2, 'preFoo'), - array($listener3, 'preFoo'), - array($listener1, 'preFoo'), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); - } - - public function testGetAllListenersSortsByPriority() - { - $listener1 = new TestEventListener(); - $listener2 = new TestEventListener(); - $listener3 = new TestEventListener(); - $listener4 = new TestEventListener(); - $listener5 = new TestEventListener(); - $listener6 = new TestEventListener(); - - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->addListener('post.foo', $listener4, -10); - $this->dispatcher->addListener('post.foo', $listener5); - $this->dispatcher->addListener('post.foo', $listener6, 10); - - $expected = array( - 'pre.foo' => array($listener3, $listener2, $listener1), - 'post.foo' => array($listener6, $listener5, $listener4), - ); - - $this->assertSame($expected, $this->dispatcher->getListeners()); - } - - public function testDispatch() - { - $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); - $this->dispatcher->dispatch(self::preFoo); - $this->assertTrue($this->listener->preFooInvoked); - $this->assertFalse($this->listener->postFooInvoked); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); - $event = new Event(); - $return = $this->dispatcher->dispatch(self::preFoo, $event); - $this->assertEquals('pre.foo', $event->getName()); - $this->assertSame($event, $return); - } - - public function testDispatchForClosure() - { - $invoked = 0; - $listener = function () use (&$invoked) { - $invoked++; - }; - $this->dispatcher->addListener('pre.foo', $listener); - $this->dispatcher->addListener('post.foo', $listener); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(1, $invoked); - } - - public function testStopEventPropagation() - { - $otherListener = new TestEventListener(); - - // postFoo() stops the propagation, so only one listener should - // be executed - // Manually set priority to enforce $this->listener to be called first - $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); - $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); - $this->dispatcher->dispatch(self::postFoo); - $this->assertTrue($this->listener->postFooInvoked); - $this->assertFalse($otherListener->postFooInvoked); - } - - public function testDispatchByPriority() - { - $invoked = array(); - $listener1 = function () use (&$invoked) { - $invoked[] = '1'; - }; - $listener2 = function () use (&$invoked) { - $invoked[] = '2'; - }; - $listener3 = function () use (&$invoked) { - $invoked[] = '3'; - }; - $this->dispatcher->addListener('pre.foo', $listener1, -10); - $this->dispatcher->addListener('pre.foo', $listener2); - $this->dispatcher->addListener('pre.foo', $listener3, 10); - $this->dispatcher->dispatch(self::preFoo); - $this->assertEquals(array('3', '2', '1'), $invoked); - } - - public function testRemoveListener() - { - $this->dispatcher->addListener('pre.bar', $this->listener); - $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('pre.bar', $this->listener); - $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); - $this->dispatcher->removeListener('notExists', $this->listener); - } - - public function testAddSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testAddSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); - } - - public function testAddSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - - $listeners = $this->dispatcher->getListeners('pre.foo'); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $listeners); - $this->assertEquals('preFoo2', $listeners[0][1]); - } - - public function testRemoveSubscriber() - { - $eventSubscriber = new TestEventSubscriber(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); - } - - public function testRemoveSubscriberWithPriorities() - { - $eventSubscriber = new TestEventSubscriberWithPriorities(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testRemoveSubscriberWithMultipleListeners() - { - $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); - $this->dispatcher->addSubscriber($eventSubscriber); - $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); - $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); - $this->dispatcher->removeSubscriber($eventSubscriber); - $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); - } - - public function testEventReceivesTheDispatcherInstance() - { - $dispatcher = null; - $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { - $dispatcher = $event->getDispatcher(); - }); - $this->dispatcher->dispatch('test'); - $this->assertSame($this->dispatcher, $dispatcher); - } - - public function testEventReceivesTheDispatcherInstanceAsArgument() - { - $listener = new TestWithDispatcher(); - $this->dispatcher->addListener('test', array($listener, 'foo')); - $this->assertNull($listener->name); - $this->assertNull($listener->dispatcher); - $this->dispatcher->dispatch('test'); - $this->assertEquals('test', $listener->name); - $this->assertSame($this->dispatcher, $listener->dispatcher); - } - - /** - * @see https://bugs.php.net/bug.php?id=62976 - * - * This bug affects: - * - The PHP 5.3 branch for versions < 5.3.18 - * - The PHP 5.4 branch for versions < 5.4.8 - * - The PHP 5.5 branch is not affected - */ - public function testWorkaroundForPhpBug62976() - { - $dispatcher = new EventDispatcher(); - $dispatcher->addListener('bug.62976', new CallableClass()); - $dispatcher->removeListener('bug.62976', function () {}); - $this->assertTrue($dispatcher->hasListeners('bug.62976')); - } -} - -class CallableClass -{ - public function __invoke() - { - } -} - -class TestEventListener -{ - public $preFooInvoked = false; - public $postFooInvoked = false; - - /* Listener methods */ - - public function preFoo(Event $e) - { - $this->preFooInvoked = true; - } - - public function postFoo(Event $e) - { - $this->postFooInvoked = true; - - $e->stopPropagation(); - } -} - -class TestWithDispatcher -{ - public $name; - public $dispatcher; - - public function foo(Event $e, $name, $dispatcher) - { - $this->name = $name; - $this->dispatcher = $dispatcher; - } -} - -class TestEventSubscriber implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); - } -} - -class TestEventSubscriberWithPriorities implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array( - 'pre.foo' => array('preFoo', 10), - 'post.foo' => array('postFoo'), - ); - } -} - -class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return array('pre.foo' => array( - array('preFoo1'), - array('preFoo2', 10) - )); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php deleted file mode 100644 index 7a20fe6bf..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/EventTest.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\EventDispatcher; - -/** - * Test class for Event. - */ -class EventTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \Symfony\Component\EventDispatcher\Event - */ - protected $event; - - /** - * @var \Symfony\Component\EventDispatcher\EventDispatcher - */ - protected $dispatcher; - - /** - * Sets up the fixture, for example, opens a network connection. - * This method is called before a test is executed. - */ - protected function setUp() - { - $this->event = new Event(); - $this->dispatcher = new EventDispatcher(); - } - - /** - * Tears down the fixture, for example, closes a network connection. - * This method is called after a test is executed. - */ - protected function tearDown() - { - $this->event = null; - $this->dispatcher = null; - } - - public function testIsPropagationStopped() - { - $this->assertFalse($this->event->isPropagationStopped()); - } - - public function testStopPropagationAndIsPropagationStopped() - { - $this->event->stopPropagation(); - $this->assertTrue($this->event->isPropagationStopped()); - } - - public function testSetDispatcher() - { - $this->event->setDispatcher($this->dispatcher); - $this->assertSame($this->dispatcher, $this->event->getDispatcher()); - } - - public function testGetDispatcher() - { - $this->assertNull($this->event->getDispatcher()); - } - - public function testGetName() - { - $this->assertNull($this->event->getName()); - } - - public function testSetName() - { - $this->event->setName('foo'); - $this->assertEquals('foo', $this->event->getName()); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php deleted file mode 100644 index 5dfda92f2..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/GenericEventTest.php +++ /dev/null @@ -1,140 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\GenericEvent; - -/** - * Test class for Event. - */ -class GenericEventTest extends \PHPUnit_Framework_TestCase -{ - - /** - * @var GenericEvent - */ - private $event; - - private $subject; - - /** - * Prepares the environment before running a test. - */ - protected function setUp() - { - parent::setUp(); - - $this->subject = new \stdClass(); - $this->event = new GenericEvent($this->subject, array('name' => 'Event')); - } - - /** - * Cleans up the environment after running a test. - */ - protected function tearDown() - { - $this->subject = null; - $this->event = null; - - parent::tearDown(); - } - - public function testConstruct() - { - $this->assertEquals($this->event, new GenericEvent($this->subject, array('name' => 'Event'))); - } - - /** - * Tests Event->getArgs() - */ - public function testGetArguments() - { - // test getting all - $this->assertSame(array('name' => 'Event'), $this->event->getArguments()); - } - - public function testSetArguments() - { - $result = $this->event->setArguments(array('foo' => 'bar')); - $this->assertAttributeSame(array('foo' => 'bar'), 'arguments', $this->event); - $this->assertSame($this->event, $result); - } - - public function testSetArgument() - { - $result = $this->event->setArgument('foo2', 'bar2'); - $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); - $this->assertEquals($this->event, $result); - } - - public function testGetArgument() - { - // test getting key - $this->assertEquals('Event', $this->event->getArgument('name')); - } - - /** - * @expectedException \InvalidArgumentException - */ - public function testGetArgException() - { - $this->event->getArgument('nameNotExist'); - } - - public function testOffsetGet() - { - // test getting key - $this->assertEquals('Event', $this->event['name']); - - // test getting invalid arg - $this->setExpectedException('InvalidArgumentException'); - $this->assertFalse($this->event['nameNotExist']); - } - - public function testOffsetSet() - { - $this->event['foo2'] = 'bar2'; - $this->assertAttributeSame(array('name' => 'Event', 'foo2' => 'bar2'), 'arguments', $this->event); - } - - public function testOffsetUnset() - { - unset($this->event['name']); - $this->assertAttributeSame(array(), 'arguments', $this->event); - } - - public function testOffsetIsset() - { - $this->assertTrue(isset($this->event['name'])); - $this->assertFalse(isset($this->event['nameNotExist'])); - } - - public function testHasArgument() - { - $this->assertTrue($this->event->hasArgument('name')); - $this->assertFalse($this->event->hasArgument('nameNotExist')); - } - - public function testGetSubject() - { - $this->assertSame($this->subject, $this->event->getSubject()); - } - - public function testHasIterator() - { - $data = array(); - foreach ($this->event as $key => $value) { - $data[$key] = $value; - } - $this->assertEquals(array('name' => 'Event'), $data); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php deleted file mode 100644 index 80a7e43be..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/Tests/ImmutableEventDispatcherTest.php +++ /dev/null @@ -1,105 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\EventDispatcher\Tests; - -use Symfony\Component\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\ImmutableEventDispatcher; - -/** - * @author Bernhard Schussek - */ -class ImmutableEventDispatcherTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - private $innerDispatcher; - - /** - * @var ImmutableEventDispatcher - */ - private $dispatcher; - - protected function setUp() - { - $this->innerDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); - $this->dispatcher = new ImmutableEventDispatcher($this->innerDispatcher); - } - - public function testDispatchDelegates() - { - $event = new Event(); - - $this->innerDispatcher->expects($this->once()) - ->method('dispatch') - ->with('event', $event) - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->dispatch('event', $event)); - } - - public function testGetListenersDelegates() - { - $this->innerDispatcher->expects($this->once()) - ->method('getListeners') - ->with('event') - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->getListeners('event')); - } - - public function testHasListenersDelegates() - { - $this->innerDispatcher->expects($this->once()) - ->method('hasListeners') - ->with('event') - ->will($this->returnValue('result')); - - $this->assertSame('result', $this->dispatcher->hasListeners('event')); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testAddListenerDisallowed() - { - $this->dispatcher->addListener('event', function () { return 'foo'; }); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testAddSubscriberDisallowed() - { - $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); - - $this->dispatcher->addSubscriber($subscriber); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testRemoveListenerDisallowed() - { - $this->dispatcher->removeListener('event', function () { return 'foo'; }); - } - - /** - * @expectedException \BadMethodCallException - */ - public function testRemoveSubscriberDisallowed() - { - $subscriber = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); - - $this->dispatcher->removeSubscriber($subscriber); - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json deleted file mode 100644 index e748c506c..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/composer.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "symfony/event-dispatcher", - "type": "library", - "description": "Symfony EventDispatcher Component", - "keywords": [], - "homepage": "http://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~2.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "autoload": { - "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } - }, - "target-dir": "Symfony/Component/EventDispatcher", - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - } -} diff --git a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist b/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist deleted file mode 100644 index 0c3de4f7b..000000000 --- a/core/lib/symfony/event-dispatcher/Symfony/Component/EventDispatcher/phpunit.xml.dist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - ./Tests/ - - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md deleted file mode 100644 index 5b5cd6a6c..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -CHANGELOG -========= - -2.3.12 ------- - - * deprecated dumpFile() file mode argument. - -2.3.0 ------ - - * added the dumpFile() method to atomically write files - -2.2.0 ------ - - * added a delete option for the mirror() method - -2.1.0 ------ - - * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value - * created the component diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php deleted file mode 100644 index c212e664d..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/ExceptionInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception interface for all exceptions thrown by the component. - * - * @author Romain Neutron - * - * @api - */ -interface ExceptionInterface -{ -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php deleted file mode 100644 index 15533db40..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/FileNotFoundException.php +++ /dev/null @@ -1,34 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception class thrown when a file couldn't be found - * - * @author Fabien Potencier - * @author Christian Gärtner - */ -class FileNotFoundException extends IOException -{ - public function __construct($message = null, $code = 0, \Exception $previous = null, $path = null) - { - if (null === $message) { - if (null === $path) { - $message = 'File could not be found.'; - } else { - $message = sprintf('File "%s" could not be found.', $path); - } - } - - parent::__construct($message, $code, $previous, $path); - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php deleted file mode 100644 index 4b551af71..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOException.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * Exception class thrown when a filesystem operation failure happens - * - * @author Romain Neutron - * @author Christian Gärtner - * @author Fabien Potencier - * - * @api - */ -class IOException extends \RuntimeException implements IOExceptionInterface -{ - private $path; - - public function __construct($message, $code = 0, \Exception $previous = null, $path = null) - { - $this->path = $path; - - parent::__construct($message, $code, $previous); - } - - /** - * {@inheritdoc} - */ - public function getPath() - { - return $this->path; - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php deleted file mode 100644 index c88c76317..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Exception/IOExceptionInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Exception; - -/** - * IOException interface for file and input/output stream related exceptions thrown by the component. - * - * @author Christian Gärtner - */ -interface IOExceptionInterface extends ExceptionInterface -{ - /** - * Returns the associated path for the exception - * - * @return string The path. - */ - public function getPath(); -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php deleted file mode 100644 index ca146aa81..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Filesystem.php +++ /dev/null @@ -1,477 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Exception\FileNotFoundException; - -/** - * Provides basic utility to manipulate the file system. - * - * @author Fabien Potencier - */ -class Filesystem -{ - /** - * Copies a file. - * - * This method only copies the file if the origin file is newer than the target file. - * - * By default, if the target already exists, it is not overridden. - * - * @param string $originFile The original filename - * @param string $targetFile The target filename - * @param bool $override Whether to override an existing file or not - * - * @throws FileNotFoundException When originFile doesn't exist - * @throws IOException When copy fails - */ - public function copy($originFile, $targetFile, $override = false) - { - if (stream_is_local($originFile) && !is_file($originFile)) { - throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); - } - - $this->mkdir(dirname($targetFile)); - - if (!$override && is_file($targetFile) && null === parse_url($originFile, PHP_URL_HOST)) { - $doCopy = filemtime($originFile) > filemtime($targetFile); - } else { - $doCopy = true; - } - - if ($doCopy) { - // https://bugs.php.net/bug.php?id=64634 - $source = fopen($originFile, 'r'); - $target = fopen($targetFile, 'w'); - stream_copy_to_stream($source, $target); - fclose($source); - fclose($target); - unset($source, $target); - - if (!is_file($targetFile)) { - throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); - } - } - } - - /** - * Creates a directory recursively. - * - * @param string|array|\Traversable $dirs The directory path - * @param int $mode The directory mode - * - * @throws IOException On any directory creation failure - */ - public function mkdir($dirs, $mode = 0777) - { - foreach ($this->toIterator($dirs) as $dir) { - if (is_dir($dir)) { - continue; - } - - if (true !== @mkdir($dir, $mode, true)) { - throw new IOException(sprintf('Failed to create "%s".', $dir), 0, null, $dir); - } - } - } - - /** - * Checks the existence of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to check - * - * @return bool true if the file exists, false otherwise - */ - public function exists($files) - { - foreach ($this->toIterator($files) as $file) { - if (!file_exists($file)) { - return false; - } - } - - return true; - } - - /** - * Sets access and modification time of file. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to create - * @param int $time The touch time as a Unix timestamp - * @param int $atime The access time as a Unix timestamp - * - * @throws IOException When touch fails - */ - public function touch($files, $time = null, $atime = null) - { - foreach ($this->toIterator($files) as $file) { - $touch = $time ? @touch($file, $time, $atime) : @touch($file); - if (true !== $touch) { - throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file); - } - } - } - - /** - * Removes files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove - * - * @throws IOException When removal fails - */ - public function remove($files) - { - $files = iterator_to_array($this->toIterator($files)); - $files = array_reverse($files); - foreach ($files as $file) { - if (!file_exists($file) && !is_link($file)) { - continue; - } - - if (is_dir($file) && !is_link($file)) { - $this->remove(new \FilesystemIterator($file)); - - if (true !== @rmdir($file)) { - throw new IOException(sprintf('Failed to remove directory "%s".', $file), 0, null, $file); - } - } else { - // https://bugs.php.net/bug.php?id=52176 - if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) { - if (true !== @rmdir($file)) { - throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file); - } - } else { - if (true !== @unlink($file)) { - throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file); - } - } - } - } - } - - /** - * Change mode for an array of files or directories. - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change mode - * @param int $mode The new mode (octal) - * @param int $umask The mode mask (octal) - * @param bool $recursive Whether change the mod recursively or not - * - * @throws IOException When the change fail - */ - public function chmod($files, $mode, $umask = 0000, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); - } - if (true !== @chmod($file, $mode & ~$umask)) { - throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file); - } - } - } - - /** - * Change the owner of an array of files or directories - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change owner - * @param string $user The new owner user name - * @param bool $recursive Whether change the owner recursively or not - * - * @throws IOException When the change fail - */ - public function chown($files, $user, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chown(new \FilesystemIterator($file), $user, true); - } - if (is_link($file) && function_exists('lchown')) { - if (true !== @lchown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); - } - } else { - if (true !== @chown($file, $user)) { - throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); - } - } - } - } - - /** - * Change the group of an array of files or directories - * - * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to change group - * @param string $group The group name - * @param bool $recursive Whether change the group recursively or not - * - * @throws IOException When the change fail - */ - public function chgrp($files, $group, $recursive = false) - { - foreach ($this->toIterator($files) as $file) { - if ($recursive && is_dir($file) && !is_link($file)) { - $this->chgrp(new \FilesystemIterator($file), $group, true); - } - if (is_link($file) && function_exists('lchgrp')) { - if (true !== @lchgrp($file, $group)) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); - } - } else { - if (true !== @chgrp($file, $group)) { - throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); - } - } - } - } - - /** - * Renames a file or a directory. - * - * @param string $origin The origin filename or directory - * @param string $target The new filename or directory - * @param bool $overwrite Whether to overwrite the target if it already exists - * - * @throws IOException When target file or directory already exists - * @throws IOException When origin cannot be renamed - */ - public function rename($origin, $target, $overwrite = false) - { - // we check that target does not exist - if (!$overwrite && is_readable($target)) { - throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); - } - - if (true !== @rename($origin, $target)) { - throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); - } - } - - /** - * Creates a symbolic link or copy a directory. - * - * @param string $originDir The origin directory path - * @param string $targetDir The symbolic link name - * @param bool $copyOnWindows Whether to copy files if on Windows - * - * @throws IOException When symlink fails - */ - public function symlink($originDir, $targetDir, $copyOnWindows = false) - { - if (!function_exists('symlink') && $copyOnWindows) { - $this->mirror($originDir, $targetDir); - - return; - } - - $this->mkdir(dirname($targetDir)); - - $ok = false; - if (is_link($targetDir)) { - if (readlink($targetDir) != $originDir) { - $this->remove($targetDir); - } else { - $ok = true; - } - } - - if (!$ok) { - if (true !== @symlink($originDir, $targetDir)) { - $report = error_get_last(); - if (is_array($report)) { - if (defined('PHP_WINDOWS_VERSION_MAJOR') && false !== strpos($report['message'], 'error code(1314)')) { - throw new IOException('Unable to create symlink due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?'); - } - } - - throw new IOException(sprintf('Failed to create symbolic link from "%s" to "%s".', $originDir, $targetDir), 0, null, $targetDir); - } - } - } - - /** - * Given an existing path, convert it to a path relative to a given starting path - * - * @param string $endPath Absolute path of target - * @param string $startPath Absolute path where traversal begins - * - * @return string Path of target relative to starting path - */ - public function makePathRelative($endPath, $startPath) - { - // Normalize separators on Windows - if (defined('PHP_WINDOWS_VERSION_MAJOR')) { - $endPath = strtr($endPath, '\\', '/'); - $startPath = strtr($startPath, '\\', '/'); - } - - // Split the paths into arrays - $startPathArr = explode('/', trim($startPath, '/')); - $endPathArr = explode('/', trim($endPath, '/')); - - // Find for which directory the common path stops - $index = 0; - while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { - $index++; - } - - // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) - $depth = count($startPathArr) - $index; - - // Repeated "../" for each level need to reach the common path - $traverser = str_repeat('../', $depth); - - $endPathRemainder = implode('/', array_slice($endPathArr, $index)); - - // Construct $endPath from traversing to the common path, then to the remaining $endPath - $relativePath = $traverser.(strlen($endPathRemainder) > 0 ? $endPathRemainder.'/' : ''); - - return (strlen($relativePath) === 0) ? './' : $relativePath; - } - - /** - * Mirrors a directory to another. - * - * @param string $originDir The origin directory - * @param string $targetDir The target directory - * @param \Traversable $iterator A Traversable instance - * @param array $options An array of boolean options - * Valid options are: - * - $options['override'] Whether to override an existing file on copy or not (see copy()) - * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink()) - * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) - * - * @throws IOException When file type is unknown - */ - public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) - { - $targetDir = rtrim($targetDir, '/\\'); - $originDir = rtrim($originDir, '/\\'); - - // Iterate in destination folder to remove obsolete entries - if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { - $deleteIterator = $iterator; - if (null === $deleteIterator) { - $flags = \FilesystemIterator::SKIP_DOTS; - $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); - } - foreach ($deleteIterator as $file) { - $origin = str_replace($targetDir, $originDir, $file->getPathname()); - if (!$this->exists($origin)) { - $this->remove($file); - } - } - } - - $copyOnWindows = false; - if (isset($options['copy_on_windows']) && !function_exists('symlink')) { - $copyOnWindows = $options['copy_on_windows']; - } - - if (null === $iterator) { - $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; - $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); - } - - foreach ($iterator as $file) { - $target = str_replace($originDir, $targetDir, $file->getPathname()); - - if ($copyOnWindows) { - if (is_link($file) || is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } elseif (is_dir($file)) { - $this->mkdir($target); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } - } else { - if (is_link($file)) { - $this->symlink($file->getLinkTarget(), $target); - } elseif (is_dir($file)) { - $this->mkdir($target); - } elseif (is_file($file)) { - $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); - } else { - throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); - } - } - } - } - - /** - * Returns whether the file path is an absolute path. - * - * @param string $file A file path - * - * @return bool - */ - public function isAbsolutePath($file) - { - if (strspn($file, '/\\', 0, 1) - || (strlen($file) > 3 && ctype_alpha($file[0]) - && substr($file, 1, 1) === ':' - && (strspn($file, '/\\', 2, 1)) - ) - || null !== parse_url($file, PHP_URL_SCHEME) - ) { - return true; - } - - return false; - } - - /** - * Atomically dumps content into a file. - * - * @param string $filename The file to be written to. - * @param string $content The data to write into the file. - * @param null|int $mode The file mode (octal). If null, file permissions are not modified - * Deprecated since version 2.3.12, to be removed in 3.0. - * @throws IOException If the file cannot be written to. - */ - public function dumpFile($filename, $content, $mode = 0666) - { - $dir = dirname($filename); - - if (!is_dir($dir)) { - $this->mkdir($dir); - } elseif (!is_writable($dir)) { - throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); - } - - $tmpFile = tempnam($dir, basename($filename)); - - if (false === @file_put_contents($tmpFile, $content)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); - } - - $this->rename($tmpFile, $filename, true); - if (null !== $mode) { - $this->chmod($filename, $mode); - } - } - - /** - * @param mixed $files - * - * @return \Traversable - */ - private function toIterator($files) - { - if (!$files instanceof \Traversable) { - $files = new \ArrayObject(is_array($files) ? $files : array($files)); - } - - return $files; - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE deleted file mode 100644 index 0b3292cf9..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2014 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md deleted file mode 100644 index 7d8a4749e..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/README.md +++ /dev/null @@ -1,45 +0,0 @@ -Filesystem Component -==================== - -Filesystem provides basic utility to manipulate the file system: - -```php -copy($originFile, $targetFile, $override = false); - -$filesystem->mkdir($dirs, $mode = 0777); - -$filesystem->touch($files, $time = null, $atime = null); - -$filesystem->remove($files); - -$filesystem->chmod($files, $mode, $umask = 0000, $recursive = false); - -$filesystem->chown($files, $user, $recursive = false); - -$filesystem->chgrp($files, $group, $recursive = false); - -$filesystem->rename($origin, $target); - -$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false); - -$filesystem->makePathRelative($endPath, $startPath); - -$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()); - -$filesystem->isAbsolutePath($file); -``` - -Resources ---------- - -You can run the unit tests with the following command: - - $ cd path/to/Symfony/Component/Filesystem/ - $ composer.phar install - $ phpunit diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php deleted file mode 100644 index 53bd8db76..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/ExceptionTest.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Exception\FileNotFoundException; - -/** - * Test class for Filesystem. - */ -class ExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testGetPath() - { - $e = new IOException('', 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath(), 'The pass should be returned.'); - } - - public function testGeneratedMessage() - { - $e = new FileNotFoundException(null, 0, null, '/foo'); - $this->assertEquals('/foo', $e->getPath()); - $this->assertEquals('File "/foo" could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testGeneratedMessageWithoutPath() - { - $e = new FileNotFoundException(); - $this->assertEquals('File could not be found.', $e->getMessage(), 'A message should be generated.'); - } - - public function testCustomMessage() - { - $e = new FileNotFoundException('bar', 0, null, '/foo'); - $this->assertEquals('bar', $e->getMessage(), 'A custom message should be possible still.'); - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php deleted file mode 100644 index aad5b9991..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ /dev/null @@ -1,907 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -use Symfony\Component\Filesystem\Filesystem; - -/** - * Test class for Filesystem. - */ -class FilesystemTest extends FilesystemTestCase -{ - /** - * @var \Symfony\Component\Filesystem\Filesystem $filesystem - */ - private $filesystem = null; - - public function setUp() - { - parent::setUp(); - $this->filesystem = new Filesystem(); - } - - public function testCopyCreatesNewFile() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testCopyFails() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - } - - public function testCopyOverridesExistingFileIfModified() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - touch($targetFilePath, time() - 1000); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyDoesNotOverrideExistingFileByDefault() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('TARGET FILE', file_get_contents($targetFilePath)); - } - - public function testCopyOverridesExistingFileIfForced() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - file_put_contents($targetFilePath, 'TARGET FILE'); - - // make sure both files have the same modification time - $modificationTime = time() - 1000; - touch($sourceFilePath, $modificationTime); - touch($targetFilePath, $modificationTime); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, true); - - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyCreatesTargetDirectoryIfItDoesNotExist() - { - $sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file'; - $targetFileDirectory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $targetFilePath = $targetFileDirectory.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($sourceFilePath, 'SOURCE FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath); - - $this->assertTrue(is_dir($targetFileDirectory)); - $this->assertFileExists($targetFilePath); - $this->assertEquals('SOURCE FILE', file_get_contents($targetFilePath)); - } - - public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToNotCopy() - { - $sourceFilePath = 'http://symfony.com/images/common/logo/logo_symfony_header.png'; - $targetFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_target_file'; - - file_put_contents($targetFilePath, 'TARGET FILE'); - - $this->filesystem->copy($sourceFilePath, $targetFilePath, false); - - $this->assertFileExists($targetFilePath); - $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath)); - } - - public function testMkdirCreatesDirectoriesRecursively() - { - $directory = $this->workspace - .DIRECTORY_SEPARATOR.'directory' - .DIRECTORY_SEPARATOR.'sub_directory'; - - $this->filesystem->mkdir($directory); - - $this->assertTrue(is_dir($directory)); - } - - public function testMkdirCreatesDirectoriesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = array( - $basePath.'1', $basePath.'2', $basePath.'3' - ); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - public function testMkdirCreatesDirectoriesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $directories = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3' - )); - - $this->filesystem->mkdir($directories); - - $this->assertTrue(is_dir($basePath.'1')); - $this->assertTrue(is_dir($basePath.'2')); - $this->assertTrue(is_dir($basePath.'3')); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testMkdirCreatesDirectoriesFails() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $dir = $basePath.'2'; - - file_put_contents($dir, ''); - - $this->filesystem->mkdir($dir); - } - - public function testTouchCreatesEmptyFile() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'; - - $this->filesystem->touch($file); - - $this->assertFileExists($file); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testTouchFails() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'1'.DIRECTORY_SEPARATOR.'2'; - - $this->filesystem->touch($file); - } - - public function testTouchCreatesEmptyFilesFromArray() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = array( - $basePath.'1', $basePath.'2', $basePath.'3' - ); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testTouchCreatesEmptyFilesFromTraversableObject() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - $files = new \ArrayObject(array( - $basePath.'1', $basePath.'2', $basePath.'3' - )); - - $this->filesystem->touch($files); - - $this->assertFileExists($basePath.'1'); - $this->assertFileExists($basePath.'2'); - $this->assertFileExists($basePath.'3'); - } - - public function testRemoveCleansFilesAndDirectoriesIteratively() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $this->filesystem->remove($basePath); - - $this->assertTrue(!is_dir($basePath)); - } - - public function testRemoveCleansArrayOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = array( - $basePath.'dir', $basePath.'file' - ); - - $this->filesystem->remove($files); - - $this->assertTrue(!is_dir($basePath.'dir')); - $this->assertTrue(!is_file($basePath.'file')); - } - - public function testRemoveCleansTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file' - )); - - $this->filesystem->remove($files); - - $this->assertTrue(!is_dir($basePath.'dir')); - $this->assertTrue(!is_file($basePath.'file')); - } - - public function testRemoveIgnoresNonExistingFiles() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - - $files = array( - $basePath.'dir', $basePath.'file' - ); - - $this->filesystem->remove($files); - - $this->assertTrue(!is_dir($basePath.'dir')); - } - - public function testRemoveCleansInvalidLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - mkdir($basePath.'dir'); - // create symlink to nonexistent file - @symlink($basePath.'file', $basePath.'link'); - - $this->filesystem->remove($basePath); - - $this->assertTrue(!is_dir($basePath)); - } - - public function testFilesExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - mkdir($basePath); - touch($basePath.'file1'); - mkdir($basePath.'folder'); - - $this->assertTrue($this->filesystem->exists($basePath.'file1')); - $this->assertTrue($this->filesystem->exists($basePath.'folder')); - } - - public function testFilesExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file' - )); - - $this->assertTrue($this->filesystem->exists($files)); - } - - public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR; - - mkdir($basePath.'dir'); - touch($basePath.'file'); - touch($basePath.'file2'); - - $files = new \ArrayObject(array( - $basePath.'dir', $basePath.'file', $basePath.'file2' - )); - - unlink($basePath.'file'); - - $this->assertFalse($this->filesystem->exists($files)); - } - - public function testInvalidFileNotExists() - { - $basePath = $this->workspace.DIRECTORY_SEPARATOR.'directory'.DIRECTORY_SEPARATOR; - - $this->assertFalse($this->filesystem->exists($basePath.time())); - } - - public function testChmodChangesFileMode() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400); - $this->filesystem->chmod($dir, 0753); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(400, $file); - } - - public function testChmodWrongMod() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($dir); - - $this->filesystem->chmod($dir, 'Wrongmode'); - } - - public function testChmodRecursive() - { - $this->markAsSkippedIfChmodIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0400, 0000, true); - $this->filesystem->chmod($dir, 0753, 0000, true); - - $this->assertFilePermissions(753, $dir); - $this->assertFilePermissions(753, $file); - } - - public function testChmodAppliesUmask() - { - $this->markAsSkippedIfChmodIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chmod($file, 0770, 0022); - $this->assertFilePermissions(750, $file); - } - - public function testChmodChangesModeOfArrayOfFiles() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = array($directory, $file); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChmodChangesModeOfTraversableFileObject() - { - $this->markAsSkippedIfChmodIsMissing(); - - $directory = $this->workspace.DIRECTORY_SEPARATOR.'directory'; - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $files = new \ArrayObject(array($directory, $file)); - - mkdir($directory); - touch($file); - - $this->filesystem->chmod($files, 0753); - - $this->assertFilePermissions(753, $file); - $this->assertFilePermissions(753, $directory); - } - - public function testChown() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, $this->getFileOwner($dir)); - } - - public function testChownRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chown($dir, $this->getFileOwner($dir), true); - } - - public function testChownSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, $this->getFileOwner($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChownFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testChgrp() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir)); - } - - public function testChgrpRecursive() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - $file = $dir.DIRECTORY_SEPARATOR.'file'; - touch($file); - - $this->filesystem->chgrp($dir, $this->getFileGroup($dir), true); - } - - public function testChgrpSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, $this->getFileGroup($link)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpSymlinkFails() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testChgrpFail() - { - $this->markAsSkippedIfPosixIsMissing(); - - $dir = $this->workspace.DIRECTORY_SEPARATOR.'dir'; - mkdir($dir); - - $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); - } - - public function testRename() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - touch($file); - - $this->filesystem->rename($file, $newPath); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionIfTargetAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath); - } - - public function testRenameOverwritesTheTargetIfItAlreadyExists() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - touch($file); - touch($newPath); - - $this->filesystem->rename($file, $newPath, true); - - $this->assertFileNotExists($file); - $this->assertFileExists($newPath); - } - - /** - * @expectedException \Symfony\Component\Filesystem\Exception\IOException - */ - public function testRenameThrowsExceptionOnError() - { - $file = $this->workspace.DIRECTORY_SEPARATOR.uniqid(); - $newPath = $this->workspace.DIRECTORY_SEPARATOR.'new_file'; - - $this->filesystem->rename($file, $newPath); - } - - public function testSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - /** - * @depends testSymlink - */ - public function testRemoveSymlink() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - $this->filesystem->remove($link); - - $this->assertTrue(!is_link($link)); - } - - public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($this->workspace, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkIsNotOverwrittenIfAlreadyCreated() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link = $this->workspace.DIRECTORY_SEPARATOR.'link'; - - touch($file); - symlink($file, $link); - - $this->filesystem->symlink($file, $link); - - $this->assertTrue(is_link($link)); - $this->assertEquals($file, readlink($link)); - } - - public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $file = $this->workspace.DIRECTORY_SEPARATOR.'file'; - $link1 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'link'; - $link2 = $this->workspace.DIRECTORY_SEPARATOR.'dir'.DIRECTORY_SEPARATOR.'subdir'.DIRECTORY_SEPARATOR.'link'; - - touch($file); - - $this->filesystem->symlink($file, $link1); - $this->filesystem->symlink($file, $link2); - - $this->assertTrue(is_link($link1)); - $this->assertEquals($file, readlink($link1)); - $this->assertTrue(is_link($link2)); - $this->assertEquals($file, readlink($link2)); - } - - /** - * @dataProvider providePathsForMakePathRelative - */ - public function testMakePathRelative($endPath, $startPath, $expectedPath) - { - $path = $this->filesystem->makePathRelative($endPath, $startPath); - - $this->assertEquals($expectedPath, $path); - } - - /** - * @return array - */ - public function providePathsForMakePathRelative() - { - $paths = array( - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), - array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), - array('var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component', '../../../'), - array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), - array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), - array('/aa/bb', '/aa/bb', './'), - array('/aa/bb', '/aa/bb/', './'), - array('/aa/bb/', '/aa/bb', './'), - array('/aa/bb/', '/aa/bb/', './'), - array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), - array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), - array('/aa/bb/cc', '/aa', 'bb/cc/'), - array('/aa/bb/cc', '/aa/', 'bb/cc/'), - array('/aa/bb/cc/', '/aa', 'bb/cc/'), - array('/aa/bb/cc/', '/aa/', 'bb/cc/'), - array('/a/aab/bb', '/a/aa', '../aab/bb/'), - array('/a/aab/bb', '/a/aa/', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa', '../aab/bb/'), - array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), - ); - - if (defined('PHP_WINDOWS_VERSION_MAJOR')) { - $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); - } - - return $paths; - } - - public function testMirrorCopiesFilesAndDirectoriesRecursively() - { - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - $directory = $sourcePath.'directory'.DIRECTORY_SEPARATOR; - $file1 = $directory.'file1'; - $file2 = $sourcePath.'file2'; - - mkdir($sourcePath); - mkdir($directory); - file_put_contents($file1, 'FILE1'); - file_put_contents($file2, 'FILE2'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertTrue(is_dir($targetPath.'directory')); - $this->assertFileEquals($file1, $targetPath.'directory'.DIRECTORY_SEPARATOR.'file1'); - $this->assertFileEquals($file2, $targetPath.'file2'); - - $this->filesystem->remove($file1); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - file_put_contents($file1, 'FILE1'); - - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertTrue($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - - $this->filesystem->remove($directory); - $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); - $this->assertFalse($this->filesystem->exists($targetPath.'directory')); - $this->assertFalse($this->filesystem->exists($targetPath.'directory'.DIRECTORY_SEPARATOR.'file1')); - } - - public function testMirrorCopiesLinks() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath); - file_put_contents($sourcePath.'file1', 'FILE1'); - symlink($sourcePath.'file1', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'file1', $targetPath.DIRECTORY_SEPARATOR.'link1'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - public function testMirrorCopiesLinkedDirectoryContents() - { - $this->markAsSkippedIfSymlinkIsMissing(); - - $sourcePath = $this->workspace.DIRECTORY_SEPARATOR.'source'.DIRECTORY_SEPARATOR; - - mkdir($sourcePath.'nested/', 0777, true); - file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); - // Note: We symlink directory, not file - symlink($sourcePath.'nested', $sourcePath.'link1'); - - $targetPath = $this->workspace.DIRECTORY_SEPARATOR.'target'.DIRECTORY_SEPARATOR; - - $this->filesystem->mirror($sourcePath, $targetPath); - - $this->assertTrue(is_dir($targetPath)); - $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.DIRECTORY_SEPARATOR.'link1/file1.txt'); - $this->assertTrue(is_link($targetPath.DIRECTORY_SEPARATOR.'link1')); - } - - /** - * @dataProvider providePathsForIsAbsolutePath - */ - public function testIsAbsolutePath($path, $expectedResult) - { - $result = $this->filesystem->isAbsolutePath($path); - - $this->assertEquals($expectedResult, $result); - } - - /** - * @return array - */ - public function providePathsForIsAbsolutePath() - { - return array( - array('/var/lib', true), - array('c:\\\\var\\lib', true), - array('\\var\\lib', true), - array('var/lib', false), - array('../var/lib', false), - array('', false), - array(null, false) - ); - } - - public function testDumpFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar', 0753); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - - // skip mode check on Windows - if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { - $this->assertFilePermissions(753, $filename); - } - } - - public function testDumpFileWithNullMode() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt'; - - $this->filesystem->dumpFile($filename, 'bar', null); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - - // skip mode check on Windows - if (!defined('PHP_WINDOWS_VERSION_MAJOR')) { - $this->assertFilePermissions(600, $filename); - } - } - - public function testDumpFileOverwritesAnExistingFile() - { - $filename = $this->workspace.DIRECTORY_SEPARATOR.'foo.txt'; - file_put_contents($filename, 'FOO BAR'); - - $this->filesystem->dumpFile($filename, 'bar'); - - $this->assertFileExists($filename); - $this->assertSame('bar', file_get_contents($filename)); - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php deleted file mode 100644 index b4b223018..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/Tests/FilesystemTestCase.php +++ /dev/null @@ -1,125 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Filesystem\Tests; - -class FilesystemTestCase extends \PHPUnit_Framework_TestCase -{ - /** - * @var string $workspace - */ - protected $workspace = null; - - protected static $symlinkOnWindows = null; - - public static function setUpBeforeClass() - { - if (defined('PHP_WINDOWS_VERSION_MAJOR')) { - static::$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)')) { - static::$symlinkOnWindows = false; - } - } - } - } - - public function setUp() - { - $this->workspace = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.time().rand(0, 1000); - mkdir($this->workspace, 0777, true); - $this->workspace = realpath($this->workspace); - } - - public function tearDown() - { - $this->clean($this->workspace); - } - - /** - * @param string $file - */ - protected 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); - } - } - - /** - * @param int $expectedFilePerms expected file permissions as three digits (i.e. 755) - * @param string $filePath - */ - protected function assertFilePermissions($expectedFilePerms, $filePath) - { - $actualFilePerms = (int) substr(sprintf('%o', fileperms($filePath)), -3); - $this->assertEquals( - $expectedFilePerms, - $actualFilePerms, - sprintf('File permissions for %s must be %s. Actual %s', $filePath, $expectedFilePerms, $actualFilePerms) - ); - } - - protected function getFileOwner($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getpwuid($infos['uid'])) { - return $datas['name']; - } - } - - protected function getFileGroup($filepath) - { - $this->markAsSkippedIfPosixIsMissing(); - - $infos = stat($filepath); - if ($datas = posix_getgrgid($infos['gid'])) { - return $datas['name']; - } - } - - protected function markAsSkippedIfSymlinkIsMissing() - { - if (!function_exists('symlink')) { - $this->markTestSkipped('symlink is not supported'); - } - - if (defined('PHP_WINDOWS_VERSION_MAJOR') && false === static::$symlinkOnWindows) { - $this->markTestSkipped('symlink requires "Create symbolic links" privilege on windows'); - } - } - - protected function markAsSkippedIfChmodIsMissing() - { - if (defined('PHP_WINDOWS_VERSION_MAJOR')) { - $this->markTestSkipped('chmod is not supported on windows'); - } - } - - protected function markAsSkippedIfPosixIsMissing() - { - if (defined('PHP_WINDOWS_VERSION_MAJOR') || !function_exists('posix_isatty')) { - $this->markTestSkipped('Posix is not supported'); - } - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json deleted file mode 100644 index bab7c4fb9..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "symfony/filesystem", - "type": "library", - "description": "Symfony Filesystem Component", - "keywords": [], - "homepage": "http://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.3.3" - }, - "autoload": { - "psr-0": { "Symfony\\Component\\Filesystem\\": "" } - }, - "target-dir": "Symfony/Component/Filesystem", - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - } -} diff --git a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist b/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist deleted file mode 100644 index ef0bf9541..000000000 --- a/core/lib/symfony/filesystem/Symfony/Component/Filesystem/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - - - - diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore b/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php deleted file mode 100644 index bdb04f535..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractAdapter.php +++ /dev/null @@ -1,236 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -/** - * Interface for finder engine implementations. - * - * @author Jean-François Simon - */ -abstract class AbstractAdapter implements AdapterInterface -{ - protected $followLinks = false; - protected $mode = 0; - protected $minDepth = 0; - protected $maxDepth = PHP_INT_MAX; - protected $exclude = array(); - protected $names = array(); - protected $notNames = array(); - protected $contains = array(); - protected $notContains = array(); - protected $sizes = array(); - protected $dates = array(); - protected $filters = array(); - protected $sort = false; - protected $paths = array(); - protected $notPaths = array(); - protected $ignoreUnreadableDirs = false; - - private static $areSupported = array(); - - /** - * {@inheritdoc} - */ - public function isSupported() - { - $name = $this->getName(); - - if (!array_key_exists($name, self::$areSupported)) { - self::$areSupported[$name] = $this->canBeUsed(); - } - - return self::$areSupported[$name]; - } - - /** - * {@inheritdoc} - */ - public function setFollowLinks($followLinks) - { - $this->followLinks = $followLinks; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setMode($mode) - { - $this->mode = $mode; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setDepths(array $depths) - { - $this->minDepth = 0; - $this->maxDepth = PHP_INT_MAX; - - foreach ($depths as $comparator) { - switch ($comparator->getOperator()) { - case '>': - $this->minDepth = $comparator->getTarget() + 1; - break; - case '>=': - $this->minDepth = $comparator->getTarget(); - break; - case '<': - $this->maxDepth = $comparator->getTarget() - 1; - break; - case '<=': - $this->maxDepth = $comparator->getTarget(); - break; - default: - $this->minDepth = $this->maxDepth = $comparator->getTarget(); - } - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setExclude(array $exclude) - { - $this->exclude = $exclude; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setNames(array $names) - { - $this->names = $names; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setNotNames(array $notNames) - { - $this->notNames = $notNames; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setContains(array $contains) - { - $this->contains = $contains; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setNotContains(array $notContains) - { - $this->notContains = $notContains; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setSizes(array $sizes) - { - $this->sizes = $sizes; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setDates(array $dates) - { - $this->dates = $dates; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setFilters(array $filters) - { - $this->filters = $filters; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setSort($sort) - { - $this->sort = $sort; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setPath(array $paths) - { - $this->paths = $paths; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function setNotPath(array $notPaths) - { - $this->notPaths = $notPaths; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function ignoreUnreadableDirs($ignore = true) - { - $this->ignoreUnreadableDirs = (bool) $ignore; - - return $this; - } - - /** - * Returns whether the adapter is supported in the current environment. - * - * This method should be implemented in all adapters. Do not implement - * isSupported in the adapters as the generic implementation provides a cache - * layer. - * - * @see isSupported - * - * @return bool Whether the adapter is supported - */ - abstract protected function canBeUsed(); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php deleted file mode 100644 index 01940385d..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AbstractFindAdapter.php +++ /dev/null @@ -1,327 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -use Symfony\Component\Finder\Exception\AccessDeniedException; -use Symfony\Component\Finder\Iterator; -use Symfony\Component\Finder\Shell\Shell; -use Symfony\Component\Finder\Expression\Expression; -use Symfony\Component\Finder\Shell\Command; -use Symfony\Component\Finder\Iterator\SortableIterator; -use Symfony\Component\Finder\Comparator\NumberComparator; -use Symfony\Component\Finder\Comparator\DateComparator; - -/** - * Shell engine implementation using GNU find command. - * - * @author Jean-François Simon - */ -abstract class AbstractFindAdapter extends AbstractAdapter -{ - /** - * @var Shell - */ - protected $shell; - - /** - * Constructor. - */ - public function __construct() - { - $this->shell = new Shell(); - } - - /** - * {@inheritdoc} - */ - public function searchInDirectory($dir) - { - // having "/../" in path make find fail - $dir = realpath($dir); - - // searching directories containing or not containing strings leads to no result - if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode && ($this->contains || $this->notContains)) { - return new Iterator\FilePathsIterator(array(), $dir); - } - - $command = Command::create(); - $find = $this->buildFindCommand($command, $dir); - - if ($this->followLinks) { - $find->add('-follow'); - } - - $find->add('-mindepth')->add($this->minDepth + 1); - - if (PHP_INT_MAX !== $this->maxDepth) { - $find->add('-maxdepth')->add($this->maxDepth + 1); - } - - if (Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES === $this->mode) { - $find->add('-type d'); - } elseif (Iterator\FileTypeFilterIterator::ONLY_FILES === $this->mode) { - $find->add('-type f'); - } - - $this->buildNamesFiltering($find, $this->names); - $this->buildNamesFiltering($find, $this->notNames, true); - $this->buildPathsFiltering($find, $dir, $this->paths); - $this->buildPathsFiltering($find, $dir, $this->notPaths, true); - $this->buildSizesFiltering($find, $this->sizes); - $this->buildDatesFiltering($find, $this->dates); - - $useGrep = $this->shell->testCommand('grep') && $this->shell->testCommand('xargs'); - $useSort = is_int($this->sort) && $this->shell->testCommand('sort') && $this->shell->testCommand('cut'); - - if ($useGrep && ($this->contains || $this->notContains)) { - $grep = $command->ins('grep'); - $this->buildContentFiltering($grep, $this->contains); - $this->buildContentFiltering($grep, $this->notContains, true); - } - - if ($useSort) { - $this->buildSorting($command, $this->sort); - } - - $command->setErrorHandler( - $this->ignoreUnreadableDirs - // If directory is unreadable and finder is set to ignore it, `stderr` is ignored. - ? function ($stderr) { return; } - : function ($stderr) { throw new AccessDeniedException($stderr); } - ); - - $paths = $this->shell->testCommand('uniq') ? $command->add('| uniq')->execute() : array_unique($command->execute()); - $iterator = new Iterator\FilePathsIterator($paths, $dir); - - if ($this->exclude) { - $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); - } - - if (!$useGrep && ($this->contains || $this->notContains)) { - $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); - } - - if ($this->filters) { - $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); - } - - if (!$useSort && $this->sort) { - $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); - $iterator = $iteratorAggregate->getIterator(); - } - - return $iterator; - } - - /** - * {@inheritdoc} - */ - protected function canBeUsed() - { - return $this->shell->testCommand('find'); - } - - /** - * @param Command $command - * @param string $dir - * - * @return Command - */ - protected function buildFindCommand(Command $command, $dir) - { - return $command - ->ins('find') - ->add('find ') - ->arg($dir) - ->add('-noleaf'); // the -noleaf option is required for filesystems that don't follow the '.' and '..' conventions - } - - /** - * @param Command $command - * @param string[] $names - * @param bool $not - */ - private function buildNamesFiltering(Command $command, array $names, $not = false) - { - if (0 === count($names)) { - return; - } - - $command->add($not ? '-not' : null)->cmd('('); - - foreach ($names as $i => $name) { - $expr = Expression::create($name); - - // Find does not support expandable globs ("*.{a,b}" syntax). - if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { - $expr = Expression::create($expr->getGlob()->toRegex(false)); - } - - // Fixes 'not search' and 'full path matching' regex problems. - // - Jokers '.' are replaced by [^/]. - // - We add '[^/]*' before and after regex (if no ^|$ flags are present). - if ($expr->isRegex()) { - $regex = $expr->getRegex(); - $regex->prepend($regex->hasStartFlag() ? '/' : '/[^/]*') - ->setStartFlag(false) - ->setStartJoker(true) - ->replaceJokers('[^/]'); - if (!$regex->hasEndFlag() || $regex->hasEndJoker()) { - $regex->setEndJoker(false)->append('[^/]*'); - } - } - - $command - ->add($i > 0 ? '-or' : null) - ->add($expr->isRegex() - ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') - : ($expr->isCaseSensitive() ? '-name' : '-iname') - ) - ->arg($expr->renderPattern()); - } - - $command->cmd(')'); - } - - /** - * @param Command $command - * @param string $dir - * @param string[] $paths - * @param bool $not - */ - private function buildPathsFiltering(Command $command, $dir, array $paths, $not = false) - { - if (0 === count($paths)) { - return; - } - - $command->add($not ? '-not' : null)->cmd('('); - - foreach ($paths as $i => $path) { - $expr = Expression::create($path); - - // Find does not support expandable globs ("*.{a,b}" syntax). - if ($expr->isGlob() && $expr->getGlob()->isExpandable()) { - $expr = Expression::create($expr->getGlob()->toRegex(false)); - } - - // Fixes 'not search' regex problems. - if ($expr->isRegex()) { - $regex = $expr->getRegex(); - $regex->prepend($regex->hasStartFlag() ? $dir.DIRECTORY_SEPARATOR : '.*')->setEndJoker(!$regex->hasEndFlag()); - } else { - $expr->prepend('*')->append('*'); - } - - $command - ->add($i > 0 ? '-or' : null) - ->add($expr->isRegex() - ? ($expr->isCaseSensitive() ? '-regex' : '-iregex') - : ($expr->isCaseSensitive() ? '-path' : '-ipath') - ) - ->arg($expr->renderPattern()); - } - - $command->cmd(')'); - } - - /** - * @param Command $command - * @param NumberComparator[] $sizes - */ - private function buildSizesFiltering(Command $command, array $sizes) - { - foreach ($sizes as $i => $size) { - $command->add($i > 0 ? '-and' : null); - - switch ($size->getOperator()) { - case '<=': - $command->add('-size -'.($size->getTarget() + 1).'c'); - break; - case '>=': - $command->add('-size +'. ($size->getTarget() - 1).'c'); - break; - case '>': - $command->add('-size +'.$size->getTarget().'c'); - break; - case '!=': - $command->add('-size -'.$size->getTarget().'c'); - $command->add('-size +'.$size->getTarget().'c'); - case '<': - default: - $command->add('-size -'.$size->getTarget().'c'); - } - } - } - - /** - * @param Command $command - * @param DateComparator[] $dates - */ - private function buildDatesFiltering(Command $command, array $dates) - { - foreach ($dates as $i => $date) { - $command->add($i > 0 ? '-and' : null); - - $mins = (int) round((time()-$date->getTarget()) / 60); - - if (0 > $mins) { - // mtime is in the future - $command->add(' -mmin -0'); - // we will have no result so we don't need to continue - return; - } - - switch ($date->getOperator()) { - case '<=': - $command->add('-mmin +'.($mins - 1)); - break; - case '>=': - $command->add('-mmin -'.($mins + 1)); - break; - case '>': - $command->add('-mmin -'.$mins); - break; - case '!=': - $command->add('-mmin +'.$mins.' -or -mmin -'.$mins); - break; - case '<': - default: - $command->add('-mmin +'.$mins); - } - } - } - - /** - * @param Command $command - * @param string $sort - * - * @throws \InvalidArgumentException - */ - private function buildSorting(Command $command, $sort) - { - $this->buildFormatSorting($command, $sort); - } - - /** - * @param Command $command - * @param string $sort - */ - abstract protected function buildFormatSorting(Command $command, $sort); - - /** - * @param Command $command - * @param array $contains - * @param bool $not - */ - abstract protected function buildContentFiltering(Command $command, array $contains, $not = false); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php deleted file mode 100644 index e25fc3e1f..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/AdapterInterface.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -/** - * @author Jean-François Simon - */ -interface AdapterInterface -{ - /** - * @param bool $followLinks - * - * @return AdapterInterface Current instance - */ - public function setFollowLinks($followLinks); - - /** - * @param int $mode - * - * @return AdapterInterface Current instance - */ - public function setMode($mode); - - /** - * @param array $exclude - * - * @return AdapterInterface Current instance - */ - public function setExclude(array $exclude); - - /** - * @param array $depths - * - * @return AdapterInterface Current instance - */ - public function setDepths(array $depths); - - /** - * @param array $names - * - * @return AdapterInterface Current instance - */ - public function setNames(array $names); - - /** - * @param array $notNames - * - * @return AdapterInterface Current instance - */ - public function setNotNames(array $notNames); - - /** - * @param array $contains - * - * @return AdapterInterface Current instance - */ - public function setContains(array $contains); - - /** - * @param array $notContains - * - * @return AdapterInterface Current instance - */ - public function setNotContains(array $notContains); - - /** - * @param array $sizes - * - * @return AdapterInterface Current instance - */ - public function setSizes(array $sizes); - - /** - * @param array $dates - * - * @return AdapterInterface Current instance - */ - public function setDates(array $dates); - - /** - * @param array $filters - * - * @return AdapterInterface Current instance - */ - public function setFilters(array $filters); - - /** - * @param \Closure|int $sort - * - * @return AdapterInterface Current instance - */ - public function setSort($sort); - - /** - * @param array $paths - * - * @return AdapterInterface Current instance - */ - public function setPath(array $paths); - - /** - * @param array $notPaths - * - * @return AdapterInterface Current instance - */ - public function setNotPath(array $notPaths); - - /** - * @param bool $ignore - * - * @return AdapterInterface Current instance - */ - public function ignoreUnreadableDirs($ignore = true); - - /** - * @param string $dir - * - * @return \Iterator Result iterator - */ - public function searchInDirectory($dir); - - /** - * Tests adapter support for current platform. - * - * @return bool - */ - public function isSupported(); - - /** - * Returns adapter name. - * - * @return string - */ - public function getName(); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php deleted file mode 100644 index 4a25baeb6..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/BsdFindAdapter.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -use Symfony\Component\Finder\Shell\Shell; -use Symfony\Component\Finder\Shell\Command; -use Symfony\Component\Finder\Iterator\SortableIterator; -use Symfony\Component\Finder\Expression\Expression; - -/** - * Shell engine implementation using BSD find command. - * - * @author Jean-François Simon - */ -class BsdFindAdapter extends AbstractFindAdapter -{ - /** - * {@inheritdoc} - */ - public function getName() - { - return 'bsd_find'; - } - - /** - * {@inheritdoc} - */ - protected function canBeUsed() - { - return in_array($this->shell->getType(), array(Shell::TYPE_BSD, Shell::TYPE_DARWIN)) && parent::canBeUsed(); - } - - /** - * {@inheritdoc} - */ - protected function buildFormatSorting(Command $command, $sort) - { - switch ($sort) { - case SortableIterator::SORT_BY_NAME: - $command->ins('sort')->add('| sort'); - - return; - case SortableIterator::SORT_BY_TYPE: - $format = '%HT'; - break; - case SortableIterator::SORT_BY_ACCESSED_TIME: - $format = '%a'; - break; - case SortableIterator::SORT_BY_CHANGED_TIME: - $format = '%c'; - break; - case SortableIterator::SORT_BY_MODIFIED_TIME: - $format = '%m'; - break; - default: - throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); - } - - $command - ->add('-print0 | xargs -0 stat -f') - ->arg($format.'%t%N') - ->add('| sort | cut -f 2'); - } - - /** - * {@inheritdoc} - */ - protected function buildFindCommand(Command $command, $dir) - { - parent::buildFindCommand($command, $dir)->addAtIndex('-E', 1); - - return $command; - } - - /** - * {@inheritdoc} - */ - protected function buildContentFiltering(Command $command, array $contains, $not = false) - { - foreach ($contains as $contain) { - $expr = Expression::create($contain); - - // todo: avoid forking process for each $pattern by using multiple -e options - $command - ->add('| grep -v \'^$\'') - ->add('| xargs -I{} grep -I') - ->add($expr->isCaseSensitive() ? null : '-i') - ->add($not ? '-L' : '-l') - ->add('-Ee')->arg($expr->renderPattern()) - ->add('{}') - ; - } - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php deleted file mode 100644 index b72451ee1..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/GnuFindAdapter.php +++ /dev/null @@ -1,104 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -use Symfony\Component\Finder\Shell\Shell; -use Symfony\Component\Finder\Shell\Command; -use Symfony\Component\Finder\Iterator\SortableIterator; -use Symfony\Component\Finder\Expression\Expression; - -/** - * Shell engine implementation using GNU find command. - * - * @author Jean-François Simon - */ -class GnuFindAdapter extends AbstractFindAdapter -{ - /** - * {@inheritdoc} - */ - public function getName() - { - return 'gnu_find'; - } - - /** - * {@inheritdoc} - */ - protected function buildFormatSorting(Command $command, $sort) - { - switch ($sort) { - case SortableIterator::SORT_BY_NAME: - $command->ins('sort')->add('| sort'); - - return; - case SortableIterator::SORT_BY_TYPE: - $format = '%y'; - break; - case SortableIterator::SORT_BY_ACCESSED_TIME: - $format = '%A@'; - break; - case SortableIterator::SORT_BY_CHANGED_TIME: - $format = '%C@'; - break; - case SortableIterator::SORT_BY_MODIFIED_TIME: - $format = '%T@'; - break; - default: - throw new \InvalidArgumentException(sprintf('Unknown sort options: %s.', $sort)); - } - - $command - ->get('find') - ->add('-printf') - ->arg($format.' %h/%f\\n') - ->add('| sort | cut') - ->arg('-d ') - ->arg('-f2-') - ; - } - - /** - * {@inheritdoc} - */ - protected function canBeUsed() - { - return $this->shell->getType() === Shell::TYPE_UNIX && parent::canBeUsed(); - } - - /** - * {@inheritdoc} - */ - protected function buildFindCommand(Command $command, $dir) - { - return parent::buildFindCommand($command, $dir)->add('-regextype posix-extended'); - } - - /** - * {@inheritdoc} - */ - protected function buildContentFiltering(Command $command, array $contains, $not = false) - { - foreach ($contains as $contain) { - $expr = Expression::create($contain); - - // todo: avoid forking process for each $pattern by using multiple -e options - $command - ->add('| xargs -I{} -r grep -I') - ->add($expr->isCaseSensitive() ? null : '-i') - ->add($not ? '-L' : '-l') - ->add('-Ee')->arg($expr->renderPattern()) - ->add('{}') - ; - } - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php b/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php deleted file mode 100644 index 378a26acd..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Adapter/PhpAdapter.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Adapter; - -use Symfony\Component\Finder\Iterator; - -/** - * PHP finder engine implementation. - * - * @author Jean-François Simon - */ -class PhpAdapter extends AbstractAdapter -{ - /** - * {@inheritdoc} - */ - public function searchInDirectory($dir) - { - $flags = \RecursiveDirectoryIterator::SKIP_DOTS; - - if ($this->followLinks) { - $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; - } - - $iterator = new \RecursiveIteratorIterator( - new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs), - \RecursiveIteratorIterator::SELF_FIRST - ); - - if ($this->minDepth > 0 || $this->maxDepth < PHP_INT_MAX) { - $iterator = new Iterator\DepthRangeFilterIterator($iterator, $this->minDepth, $this->maxDepth); - } - - if ($this->mode) { - $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); - } - - if ($this->exclude) { - $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); - } - - if ($this->names || $this->notNames) { - $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); - } - - if ($this->contains || $this->notContains) { - $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); - } - - if ($this->sizes) { - $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); - } - - if ($this->dates) { - $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); - } - - if ($this->filters) { - $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); - } - - if ($this->sort) { - $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); - $iterator = $iteratorAggregate->getIterator(); - } - - if ($this->paths || $this->notPaths) { - $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); - } - - return $iterator; - } - - /** - * {@inheritdoc} - */ - public function getName() - { - return 'php'; - } - - /** - * {@inheritdoc} - */ - protected function canBeUsed() - { - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md b/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md deleted file mode 100644 index 7ad230813..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/CHANGELOG.md +++ /dev/null @@ -1,30 +0,0 @@ -CHANGELOG -========= - -2.3.0 ------ - - * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs()) - * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception - -2.2.0 ------ - - * added Finder::path() and Finder::notPath() methods - * added finder adapters to improve performance on specific platforms - * added support for wildcard characters (glob patterns) in the paths passed - to Finder::in() - -2.1.0 ------ - - * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and - Finder::sortByModifiedTime() - * added Countable to Finder - * added support for an array of directories as an argument to - Finder::exclude() - * added searching based on the file content via Finder::contains() and - Finder::notContains() - * added support for the != operator in the Comparator - * [BC BREAK] filter expressions (used for file name and content) are no more - considered as regexps but glob patterns when they are enclosed in '*' or '?' diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php deleted file mode 100644 index 4f5e1ffe1..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/Comparator.php +++ /dev/null @@ -1,98 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Comparator; - -/** - * Comparator. - * - * @author Fabien Potencier - */ -class Comparator -{ - private $target; - private $operator = '=='; - - /** - * Gets the target value. - * - * @return string The target value - */ - public function getTarget() - { - return $this->target; - } - - /** - * Sets the target value. - * - * @param string $target The target value - */ - public function setTarget($target) - { - $this->target = $target; - } - - /** - * Gets the comparison operator. - * - * @return string The operator - */ - public function getOperator() - { - return $this->operator; - } - - /** - * Sets the comparison operator. - * - * @param string $operator A valid operator - * - * @throws \InvalidArgumentException - */ - public function setOperator($operator) - { - if (!$operator) { - $operator = '=='; - } - - if (!in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) { - throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); - } - - $this->operator = $operator; - } - - /** - * Tests against the target. - * - * @param mixed $test A test value - * - * @return bool - */ - public function test($test) - { - switch ($this->operator) { - case '>': - return $test > $this->target; - case '>=': - return $test >= $this->target; - case '<': - return $test < $this->target; - case '<=': - return $test <= $this->target; - case '!=': - return $test != $this->target; - } - - return $test == $this->target; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php deleted file mode 100644 index 9de444f05..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/DateComparator.php +++ /dev/null @@ -1,54 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Comparator; - -/** - * DateCompare compiles date comparisons. - * - * @author Fabien Potencier - */ -class DateComparator extends Comparator -{ - - /** - * Constructor. - * - * @param string $test A comparison string - * - * @throws \InvalidArgumentException If the test is not understood - */ - public function __construct($test) - { - if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { - throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); - } - - try { - $date = new \DateTime($matches[2]); - $target = $date->format('U'); - } catch (\Exception $e) { - throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); - } - - $operator = isset($matches[1]) ? $matches[1] : '=='; - if ('since' === $operator || 'after' === $operator) { - $operator = '>'; - } - - if ('until' === $operator || 'before' === $operator) { - $operator = '<'; - } - - $this->setOperator($operator); - $this->setTarget($target); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php deleted file mode 100644 index 955cc6755..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Comparator/NumberComparator.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Comparator; - -/** - * NumberComparator compiles a simple comparison to an anonymous - * subroutine, which you can call with a value to be tested again. - * - * Now this would be very pointless, if NumberCompare didn't understand - * magnitudes. - * - * The target value may use magnitudes of kilobytes (k, ki), - * megabytes (m, mi), or gigabytes (g, gi). Those suffixed - * with an i use the appropriate 2**n version in accordance with the - * IEC standard: http://physics.nist.gov/cuu/Units/binary.html - * - * Based on the Perl Number::Compare module. - * - * @author Fabien Potencier PHP port - * @author Richard Clamp Perl version - * - * @copyright 2004-2005 Fabien Potencier - * @copyright 2002 Richard Clamp - * - * @see http://physics.nist.gov/cuu/Units/binary.html - */ -class NumberComparator extends Comparator -{ - /** - * Constructor. - * - * @param string $test A comparison string - * - * @throws \InvalidArgumentException If the test is not understood - */ - public function __construct($test) - { - if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { - throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); - } - - $target = $matches[2]; - if (!is_numeric($target)) { - throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); - } - if (isset($matches[3])) { - // magnitude - switch (strtolower($matches[3])) { - case 'k': - $target *= 1000; - break; - case 'ki': - $target *= 1024; - break; - case 'm': - $target *= 1000000; - break; - case 'mi': - $target *= 1024*1024; - break; - case 'g': - $target *= 1000000000; - break; - case 'gi': - $target *= 1024*1024*1024; - break; - } - } - - $this->setTarget($target); - $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php deleted file mode 100644 index ee195ea8d..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AccessDeniedException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -/** - * @author Jean-François Simon - */ -class AccessDeniedException extends \UnexpectedValueException -{ -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php deleted file mode 100644 index 15fa22147..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/AdapterFailureException.php +++ /dev/null @@ -1,46 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -use Symfony\Component\Finder\Adapter\AdapterInterface; - -/** - * Base exception for all adapter failures. - * - * @author Jean-François Simon - */ -class AdapterFailureException extends \RuntimeException implements ExceptionInterface -{ - /** - * @var \Symfony\Component\Finder\Adapter\AdapterInterface - */ - private $adapter; - - /** - * @param AdapterInterface $adapter - * @param string|null $message - * @param \Exception|null $previous - */ - public function __construct(AdapterInterface $adapter, $message = null, \Exception $previous = null) - { - $this->adapter = $adapter; - parent::__construct($message ?: 'Search failed with "'.$adapter->getName().'" adapter.', $previous); - } - - /** - * {@inheritdoc} - */ - public function getAdapter() - { - return $this->adapter; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php deleted file mode 100644 index bff02142d..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ExceptionInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -/** - * @author Jean-François Simon - */ -interface ExceptionInterface -{ - /** - * @return \Symfony\Component\Finder\Adapter\AdapterInterface - */ - public function getAdapter(); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php deleted file mode 100644 index 366311225..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/OperationNotPermitedException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -/** - * @author Jean-François Simon - */ -class OperationNotPermitedException extends AdapterFailureException -{ -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php b/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php deleted file mode 100644 index 2658f6a50..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Exception/ShellCommandFailureException.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Exception; - -use Symfony\Component\Finder\Adapter\AdapterInterface; -use Symfony\Component\Finder\Shell\Command; - -/** - * @author Jean-François Simon - */ -class ShellCommandFailureException extends AdapterFailureException -{ - /** - * @var Command - */ - private $command; - - /** - * @param AdapterInterface $adapter - * @param Command $command - * @param \Exception|null $previous - */ - public function __construct(AdapterInterface $adapter, Command $command, \Exception $previous = null) - { - $this->command = $command; - parent::__construct($adapter, 'Shell command failed: "'.$command->join().'".', $previous); - } - - /** - * @return Command - */ - public function getCommand() - { - return $this->command; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php deleted file mode 100644 index 8559b33e3..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Expression.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Expression; - -/** - * @author Jean-François Simon - */ -class Expression implements ValueInterface -{ - const TYPE_REGEX = 1; - const TYPE_GLOB = 2; - - /** - * @var ValueInterface - */ - private $value; - - /** - * @param string $expr - * - * @return Expression - */ - public static function create($expr) - { - return new self($expr); - } - - /** - * @param string $expr - */ - public function __construct($expr) - { - try { - $this->value = Regex::create($expr); - } catch (\InvalidArgumentException $e) { - $this->value = new Glob($expr); - } - } - - /** - * @return string - */ - public function __toString() - { - return $this->render(); - } - - /** - * {@inheritdoc} - */ - public function render() - { - return $this->value->render(); - } - - /** - * {@inheritdoc} - */ - public function renderPattern() - { - return $this->value->renderPattern(); - } - - /** - * @return bool - */ - public function isCaseSensitive() - { - return $this->value->isCaseSensitive(); - } - - /** - * @return int - */ - public function getType() - { - return $this->value->getType(); - } - - /** - * {@inheritdoc} - */ - public function prepend($expr) - { - $this->value->prepend($expr); - - return $this; - } - - /** - * {@inheritdoc} - */ - public function append($expr) - { - $this->value->append($expr); - - return $this; - } - - /** - * @return bool - */ - public function isRegex() - { - return self::TYPE_REGEX === $this->value->getType(); - } - - /** - * @return bool - */ - public function isGlob() - { - return self::TYPE_GLOB === $this->value->getType(); - } - - /** - * @throws \LogicException - * - * @return Glob - */ - public function getGlob() - { - if (self::TYPE_GLOB !== $this->value->getType()) { - throw new \LogicException('Regex can\'t be transformed to glob.'); - } - - return $this->value; - } - - /** - * @return Regex - */ - public function getRegex() - { - return self::TYPE_REGEX === $this->value->getType() ? $this->value : $this->value->toRegex(); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php deleted file mode 100644 index 3023ceea6..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Glob.php +++ /dev/null @@ -1,157 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Expression; - -/** - * @author Jean-François Simon - */ -class Glob implements ValueInterface -{ - /** - * @var string - */ - private $pattern; - - /** - * @param string $pattern - */ - public function __construct($pattern) - { - $this->pattern = $pattern; - } - - /** - * {@inheritdoc} - */ - public function render() - { - return $this->pattern; - } - - /** - * {@inheritdoc} - */ - public function renderPattern() - { - return $this->pattern; - } - - /** - * {@inheritdoc} - */ - public function getType() - { - return Expression::TYPE_GLOB; - } - - /** - * {@inheritdoc} - */ - public function isCaseSensitive() - { - return true; - } - - /** - * {@inheritdoc} - */ - public function prepend($expr) - { - $this->pattern = $expr.$this->pattern; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function append($expr) - { - $this->pattern .= $expr; - - return $this; - } - - /** - * Tests if glob is expandable ("*.{a,b}" syntax). - * - * @return bool - */ - public function isExpandable() - { - return false !== strpos($this->pattern, '{') - && false !== strpos($this->pattern, '}'); - } - - /** - * @param bool $strictLeadingDot - * @param bool $strictWildcardSlash - * - * @return Regex - */ - public function toRegex($strictLeadingDot = true, $strictWildcardSlash = true) - { - $firstByte = true; - $escaping = false; - $inCurlies = 0; - $regex = ''; - $sizeGlob = strlen($this->pattern); - for ($i = 0; $i < $sizeGlob; $i++) { - $car = $this->pattern[$i]; - if ($firstByte) { - if ($strictLeadingDot && '.' !== $car) { - $regex .= '(?=[^\.])'; - } - - $firstByte = false; - } - - if ('/' === $car) { - $firstByte = true; - } - - if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { - $regex .= "\\$car"; - } elseif ('*' === $car) { - $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); - } elseif ('?' === $car) { - $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); - } elseif ('{' === $car) { - $regex .= $escaping ? '\\{' : '('; - if (!$escaping) { - ++$inCurlies; - } - } elseif ('}' === $car && $inCurlies) { - $regex .= $escaping ? '}' : ')'; - if (!$escaping) { - --$inCurlies; - } - } elseif (',' === $car && $inCurlies) { - $regex .= $escaping ? ',' : '|'; - } elseif ('\\' === $car) { - if ($escaping) { - $regex .= '\\\\'; - $escaping = false; - } else { - $escaping = true; - } - - continue; - } else { - $regex .= $car; - } - $escaping = false; - } - - return new Regex('^'.$regex.'$'); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php deleted file mode 100644 index 2e8f507b4..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/Regex.php +++ /dev/null @@ -1,321 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Expression; - -/** - * @author Jean-François Simon - */ -class Regex implements ValueInterface -{ - const START_FLAG = '^'; - const END_FLAG = '$'; - const BOUNDARY = '~'; - const JOKER = '.*'; - const ESCAPING = '\\'; - - /** - * @var string - */ - private $pattern; - - /** - * @var array - */ - private $options; - - /** - * @var bool - */ - private $startFlag; - - /** - * @var bool - */ - private $endFlag; - - /** - * @var bool - */ - private $startJoker; - - /** - * @var bool - */ - private $endJoker; - - /** - * @param string $expr - * - * @return Regex - * - * @throws \InvalidArgumentException - */ - public static function create($expr) - { - if (preg_match('/^(.{3,}?)([imsxuADU]*)$/', $expr, $m)) { - $start = substr($m[1], 0, 1); - $end = substr($m[1], -1); - - if ( - ($start === $end && !preg_match('/[*?[:alnum:] \\\\]/', $start)) - || ($start === '{' && $end === '}') - || ($start === '(' && $end === ')') - ) { - return new self(substr($m[1], 1, -1), $m[2], $end); - } - } - - throw new \InvalidArgumentException('Given expression is not a regex.'); - } - - /** - * @param string $pattern - * @param string $options - * @param string $delimiter - */ - public function __construct($pattern, $options = '', $delimiter = null) - { - if (null !== $delimiter) { - // removes delimiter escaping - $pattern = str_replace('\\'.$delimiter, $delimiter, $pattern); - } - - $this->parsePattern($pattern); - $this->options = $options; - } - - /** - * @return string - */ - public function __toString() - { - return $this->render(); - } - - /** - * {@inheritdoc} - */ - public function render() - { - return self::BOUNDARY - .$this->renderPattern() - .self::BOUNDARY - .$this->options; - } - - /** - * {@inheritdoc} - */ - public function renderPattern() - { - return ($this->startFlag ? self::START_FLAG : '') - .($this->startJoker ? self::JOKER : '') - .str_replace(self::BOUNDARY, '\\'.self::BOUNDARY, $this->pattern) - .($this->endJoker ? self::JOKER : '') - .($this->endFlag ? self::END_FLAG : ''); - } - - /** - * {@inheritdoc} - */ - public function isCaseSensitive() - { - return !$this->hasOption('i'); - } - - /** - * {@inheritdoc} - */ - public function getType() - { - return Expression::TYPE_REGEX; - } - - /** - * {@inheritdoc} - */ - public function prepend($expr) - { - $this->pattern = $expr.$this->pattern; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function append($expr) - { - $this->pattern .= $expr; - - return $this; - } - - /** - * @param string $option - * - * @return bool - */ - public function hasOption($option) - { - return false !== strpos($this->options, $option); - } - - /** - * @param string $option - * - * @return Regex - */ - public function addOption($option) - { - if (!$this->hasOption($option)) { - $this->options.= $option; - } - - return $this; - } - - /** - * @param string $option - * - * @return Regex - */ - public function removeOption($option) - { - $this->options = str_replace($option, '', $this->options); - - return $this; - } - - /** - * @param bool $startFlag - * - * @return Regex - */ - public function setStartFlag($startFlag) - { - $this->startFlag = $startFlag; - - return $this; - } - - /** - * @return bool - */ - public function hasStartFlag() - { - return $this->startFlag; - } - - /** - * @param bool $endFlag - * - * @return Regex - */ - public function setEndFlag($endFlag) - { - $this->endFlag = (bool) $endFlag; - - return $this; - } - - /** - * @return bool - */ - public function hasEndFlag() - { - return $this->endFlag; - } - - /** - * @param bool $startJoker - * - * @return Regex - */ - public function setStartJoker($startJoker) - { - $this->startJoker = $startJoker; - - return $this; - } - - /** - * @return bool - */ - public function hasStartJoker() - { - return $this->startJoker; - } - - /** - * @param bool $endJoker - * - * @return Regex - */ - public function setEndJoker($endJoker) - { - $this->endJoker = (bool) $endJoker; - - return $this; - } - - /** - * @return bool - */ - public function hasEndJoker() - { - return $this->endJoker; - } - - /** - * @param array $replacement - * - * @return Regex - */ - public function replaceJokers($replacement) - { - $replace = function ($subject) use ($replacement) { - $subject = $subject[0]; - $replace = 0 === substr_count($subject, '\\') % 2; - - return $replace ? str_replace('.', $replacement, $subject) : $subject; - }; - - $this->pattern = preg_replace_callback('~[\\\\]*\\.~', $replace, $this->pattern); - - return $this; - } - - /** - * @param string $pattern - */ - private function parsePattern($pattern) - { - if ($this->startFlag = self::START_FLAG === substr($pattern, 0, 1)) { - $pattern = substr($pattern, 1); - } - - if ($this->startJoker = self::JOKER === substr($pattern, 0, 2)) { - $pattern = substr($pattern, 2); - } - - if ($this->endFlag = (self::END_FLAG === substr($pattern, -1) && self::ESCAPING !== substr($pattern, -2, -1))) { - $pattern = substr($pattern, 0, -1); - } - - if ($this->endJoker = (self::JOKER === substr($pattern, -2) && self::ESCAPING !== substr($pattern, -3, -2))) { - $pattern = substr($pattern, 0, -2); - } - - $this->pattern = $pattern; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php b/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php deleted file mode 100644 index 34ce0e7ce..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Expression/ValueInterface.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Expression; - -/** - * @author Jean-François Simon - */ -interface ValueInterface -{ - /** - * Renders string representation of expression. - * - * @return string - */ - public function render(); - - /** - * Renders string representation of pattern. - * - * @return string - */ - public function renderPattern(); - - /** - * Returns value case sensitivity. - * - * @return bool - */ - public function isCaseSensitive(); - - /** - * Returns expression type. - * - * @return int - */ - public function getType(); - - /** - * @param string $expr - * - * @return ValueInterface - */ - public function prepend($expr); - - /** - * @param string $expr - * - * @return ValueInterface - */ - public function append($expr); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php b/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php deleted file mode 100644 index e945fab13..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Finder.php +++ /dev/null @@ -1,829 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder; - -use Symfony\Component\Finder\Adapter\AdapterInterface; -use Symfony\Component\Finder\Adapter\GnuFindAdapter; -use Symfony\Component\Finder\Adapter\BsdFindAdapter; -use Symfony\Component\Finder\Adapter\PhpAdapter; -use Symfony\Component\Finder\Exception\ExceptionInterface; - -/** - * Finder allows to build rules to find files and directories. - * - * It is a thin wrapper around several specialized iterator classes. - * - * All rules may be invoked several times. - * - * All methods return the current Finder object to allow easy chaining: - * - * $finder = Finder::create()->files()->name('*.php')->in(__DIR__); - * - * @author Fabien Potencier - * - * @api - */ -class Finder implements \IteratorAggregate, \Countable -{ - const IGNORE_VCS_FILES = 1; - const IGNORE_DOT_FILES = 2; - - private $mode = 0; - private $names = array(); - private $notNames = array(); - private $exclude = array(); - private $filters = array(); - private $depths = array(); - private $sizes = array(); - private $followLinks = false; - private $sort = false; - private $ignore = 0; - private $dirs = array(); - private $dates = array(); - private $iterators = array(); - private $contains = array(); - private $notContains = array(); - private $adapters = array(); - private $paths = array(); - private $notPaths = array(); - private $ignoreUnreadableDirs = false; - - private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'); - - /** - * Constructor. - */ - public function __construct() - { - $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; - - $this - ->addAdapter(new GnuFindAdapter()) - ->addAdapter(new BsdFindAdapter()) - ->addAdapter(new PhpAdapter(), -50) - ->setAdapter('php') - ; - } - - /** - * Creates a new Finder. - * - * @return Finder A new Finder instance - * - * @api - */ - public static function create() - { - return new static(); - } - - /** - * Registers a finder engine implementation. - * - * @param AdapterInterface $adapter An adapter instance - * @param int $priority Highest is selected first - * - * @return Finder The current Finder instance - */ - public function addAdapter(Adapter\AdapterInterface $adapter, $priority = 0) - { - $this->adapters[$adapter->getName()] = array( - 'adapter' => $adapter, - 'priority' => $priority, - 'selected' => false, - ); - - return $this->sortAdapters(); - } - - /** - * Sets the selected adapter to the best one according to the current platform the code is run on. - * - * @return Finder The current Finder instance - */ - public function useBestAdapter() - { - $this->resetAdapterSelection(); - - return $this->sortAdapters(); - } - - /** - * Selects the adapter to use. - * - * @param string $name - * - * @throws \InvalidArgumentException - * - * @return Finder The current Finder instance - */ - public function setAdapter($name) - { - if (!isset($this->adapters[$name])) { - throw new \InvalidArgumentException(sprintf('Adapter "%s" does not exist.', $name)); - } - - $this->resetAdapterSelection(); - $this->adapters[$name]['selected'] = true; - - return $this->sortAdapters(); - } - - /** - * Removes all adapters registered in the finder. - * - * @return Finder The current Finder instance - */ - public function removeAdapters() - { - $this->adapters = array(); - - return $this; - } - - /** - * Returns registered adapters ordered by priority without extra information. - * - * @return AdapterInterface[] - */ - public function getAdapters() - { - return array_values(array_map(function (array $adapter) { - return $adapter['adapter']; - }, $this->adapters)); - } - - /** - * Restricts the matching to directories only. - * - * @return Finder The current Finder instance - * - * @api - */ - public function directories() - { - $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; - - return $this; - } - - /** - * Restricts the matching to files only. - * - * @return Finder The current Finder instance - * - * @api - */ - public function files() - { - $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; - - return $this; - } - - /** - * Adds tests for the directory depth. - * - * Usage: - * - * $finder->depth('> 1') // the Finder will start matching at level 1. - * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point. - * - * @param int $level The depth level expression - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\DepthRangeFilterIterator - * @see Symfony\Component\Finder\Comparator\NumberComparator - * - * @api - */ - public function depth($level) - { - $this->depths[] = new Comparator\NumberComparator($level); - - return $this; - } - - /** - * Adds tests for file dates (last modified). - * - * The date must be something that strtotime() is able to parse: - * - * $finder->date('since yesterday'); - * $finder->date('until 2 days ago'); - * $finder->date('> now - 2 hours'); - * $finder->date('>= 2005-10-15'); - * - * @param string $date A date rage string - * - * @return Finder The current Finder instance - * - * @see strtotime - * @see Symfony\Component\Finder\Iterator\DateRangeFilterIterator - * @see Symfony\Component\Finder\Comparator\DateComparator - * - * @api - */ - public function date($date) - { - $this->dates[] = new Comparator\DateComparator($date); - - return $this; - } - - /** - * Adds rules that files must match. - * - * You can use patterns (delimited with / sign), globs or simple strings. - * - * $finder->name('*.php') - * $finder->name('/\.php$/') // same as above - * $finder->name('test.php') - * - * @param string $pattern A pattern (a regexp, a glob, or a string) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator - * - * @api - */ - public function name($pattern) - { - $this->names[] = $pattern; - - return $this; - } - - /** - * Adds rules that files must not match. - * - * @param string $pattern A pattern (a regexp, a glob, or a string) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator - * - * @api - */ - public function notName($pattern) - { - $this->notNames[] = $pattern; - - return $this; - } - - /** - * Adds tests that file contents must match. - * - * Strings or PCRE patterns can be used: - * - * $finder->contains('Lorem ipsum') - * $finder->contains('/Lorem ipsum/i') - * - * @param string $pattern A pattern (string or regexp) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator - */ - public function contains($pattern) - { - $this->contains[] = $pattern; - - return $this; - } - - /** - * Adds tests that file contents must not match. - * - * Strings or PCRE patterns can be used: - * - * $finder->notContains('Lorem ipsum') - * $finder->notContains('/Lorem ipsum/i') - * - * @param string $pattern A pattern (string or regexp) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilecontentFilterIterator - */ - public function notContains($pattern) - { - $this->notContains[] = $pattern; - - return $this; - } - - /** - * Adds rules that filenames must match. - * - * You can use patterns (delimited with / sign) or simple strings. - * - * $finder->path('some/special/dir') - * $finder->path('/some\/special\/dir/') // same as above - * - * Use only / as dirname separator. - * - * @param string $pattern A pattern (a regexp or a string) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator - */ - public function path($pattern) - { - $this->paths[] = $pattern; - - return $this; - } - - /** - * Adds rules that filenames must not match. - * - * You can use patterns (delimited with / sign) or simple strings. - * - * $finder->notPath('some/special/dir') - * $finder->notPath('/some\/special\/dir/') // same as above - * - * Use only / as dirname separator. - * - * @param string $pattern A pattern (a regexp or a string) - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\FilenameFilterIterator - */ - public function notPath($pattern) - { - $this->notPaths[] = $pattern; - - return $this; - } - - /** - * Adds tests for file sizes. - * - * $finder->size('> 10K'); - * $finder->size('<= 1Ki'); - * $finder->size(4); - * - * @param string $size A size range string - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SizeRangeFilterIterator - * @see Symfony\Component\Finder\Comparator\NumberComparator - * - * @api - */ - public function size($size) - { - $this->sizes[] = new Comparator\NumberComparator($size); - - return $this; - } - - /** - * Excludes directories. - * - * @param string|array $dirs A directory path or an array of directories - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator - * - * @api - */ - public function exclude($dirs) - { - $this->exclude = array_merge($this->exclude, (array) $dirs); - - return $this; - } - - /** - * Excludes "hidden" directories and files (starting with a dot). - * - * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator - * - * @api - */ - public function ignoreDotFiles($ignoreDotFiles) - { - if ($ignoreDotFiles) { - $this->ignore = $this->ignore | static::IGNORE_DOT_FILES; - } else { - $this->ignore = $this->ignore & ~static::IGNORE_DOT_FILES; - } - - return $this; - } - - /** - * Forces the finder to ignore version control directories. - * - * @param bool $ignoreVCS Whether to exclude VCS files or not - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator - * - * @api - */ - public function ignoreVCS($ignoreVCS) - { - if ($ignoreVCS) { - $this->ignore = $this->ignore | static::IGNORE_VCS_FILES; - } else { - $this->ignore = $this->ignore & ~static::IGNORE_VCS_FILES; - } - - return $this; - } - - /** - * Adds VCS patterns. - * - * @see ignoreVCS - * - * @param string|string[] $pattern VCS patterns to ignore - */ - public static function addVCSPattern($pattern) - { - foreach ((array) $pattern as $p) { - self::$vcsPatterns[] = $p; - } - - self::$vcsPatterns = array_unique(self::$vcsPatterns); - } - - /** - * Sorts files and directories by an anonymous function. - * - * The anonymous function receives two \SplFileInfo instances to compare. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @param \Closure $closure An anonymous function - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sort(\Closure $closure) - { - $this->sort = $closure; - - return $this; - } - - /** - * Sorts files and directories by name. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sortByName() - { - $this->sort = Iterator\SortableIterator::SORT_BY_NAME; - - return $this; - } - - /** - * Sorts files and directories by type (directories before files), then by name. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sortByType() - { - $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; - - return $this; - } - - /** - * Sorts files and directories by the last accessed time. - * - * This is the time that the file was last accessed, read or written to. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sortByAccessedTime() - { - $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; - - return $this; - } - - /** - * Sorts files and directories by the last inode changed time. - * - * This is the time that the inode information was last modified (permissions, owner, group or other metadata). - * - * On Windows, since inode is not available, changed time is actually the file creation time. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sortByChangedTime() - { - $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; - - return $this; - } - - /** - * Sorts files and directories by the last modified time. - * - * This is the last time the actual contents of the file were last modified. - * - * This can be slow as all the matching files and directories must be retrieved for comparison. - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\SortableIterator - * - * @api - */ - public function sortByModifiedTime() - { - $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; - - return $this; - } - - /** - * Filters the iterator with an anonymous function. - * - * The anonymous function receives a \SplFileInfo and must return false - * to remove files. - * - * @param \Closure $closure An anonymous function - * - * @return Finder The current Finder instance - * - * @see Symfony\Component\Finder\Iterator\CustomFilterIterator - * - * @api - */ - public function filter(\Closure $closure) - { - $this->filters[] = $closure; - - return $this; - } - - /** - * Forces the following of symlinks. - * - * @return Finder The current Finder instance - * - * @api - */ - public function followLinks() - { - $this->followLinks = true; - - return $this; - } - - /** - * Tells finder to ignore unreadable directories. - * - * By default, scanning unreadable directories content throws an AccessDeniedException. - * - * @param bool $ignore - * - * @return Finder The current Finder instance - */ - public function ignoreUnreadableDirs($ignore = true) - { - $this->ignoreUnreadableDirs = (bool) $ignore; - - return $this; - } - - /** - * Searches files and directories which match defined rules. - * - * @param string|array $dirs A directory path or an array of directories - * - * @return Finder The current Finder instance - * - * @throws \InvalidArgumentException if one of the directories does not exist - * - * @api - */ - public function in($dirs) - { - $resolvedDirs = array(); - - foreach ((array) $dirs as $dir) { - if (is_dir($dir)) { - $resolvedDirs[] = $dir; - } elseif ($glob = glob($dir, GLOB_ONLYDIR)) { - $resolvedDirs = array_merge($resolvedDirs, $glob); - } else { - throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); - } - } - - $this->dirs = array_merge($this->dirs, $resolvedDirs); - - return $this; - } - - /** - * Returns an Iterator for the current Finder configuration. - * - * This method implements the IteratorAggregate interface. - * - * @return \Iterator An iterator - * - * @throws \LogicException if the in() method has not been called - */ - public function getIterator() - { - if (0 === count($this->dirs) && 0 === count($this->iterators)) { - throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); - } - - if (1 === count($this->dirs) && 0 === count($this->iterators)) { - return $this->searchInDirectory($this->dirs[0]); - } - - $iterator = new \AppendIterator(); - foreach ($this->dirs as $dir) { - $iterator->append($this->searchInDirectory($dir)); - } - - foreach ($this->iterators as $it) { - $iterator->append($it); - } - - return $iterator; - } - - /** - * Appends an existing set of files/directories to the finder. - * - * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array. - * - * @param mixed $iterator - * - * @return Finder The finder - * - * @throws \InvalidArgumentException When the given argument is not iterable. - */ - public function append($iterator) - { - if ($iterator instanceof \IteratorAggregate) { - $this->iterators[] = $iterator->getIterator(); - } elseif ($iterator instanceof \Iterator) { - $this->iterators[] = $iterator; - } elseif ($iterator instanceof \Traversable || is_array($iterator)) { - $it = new \ArrayIterator(); - foreach ($iterator as $file) { - $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); - } - $this->iterators[] = $it; - } else { - throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); - } - - return $this; - } - - /** - * Counts all the results collected by the iterators. - * - * @return int - */ - public function count() - { - return iterator_count($this->getIterator()); - } - - /** - * @return Finder The current Finder instance - */ - private function sortAdapters() - { - uasort($this->adapters, function (array $a, array $b) { - if ($a['selected'] || $b['selected']) { - return $a['selected'] ? -1 : 1; - } - - return $a['priority'] > $b['priority'] ? -1 : 1; - }); - - return $this; - } - - /** - * @param $dir - * - * @return \Iterator - * - * @throws \RuntimeException When none of the adapters are supported - */ - private function searchInDirectory($dir) - { - if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { - $this->exclude = array_merge($this->exclude, self::$vcsPatterns); - } - - if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { - $this->notPaths[] = '#(^|/)\..+(/|$)#'; - } - - foreach ($this->adapters as $adapter) { - if ($adapter['adapter']->isSupported()) { - try { - return $this - ->buildAdapter($adapter['adapter']) - ->searchInDirectory($dir); - } catch (ExceptionInterface $e) {} - } - } - - throw new \RuntimeException('No supported adapter found.'); - } - - /** - * @param AdapterInterface $adapter - * - * @return AdapterInterface - */ - private function buildAdapter(AdapterInterface $adapter) - { - return $adapter - ->setFollowLinks($this->followLinks) - ->setDepths($this->depths) - ->setMode($this->mode) - ->setExclude($this->exclude) - ->setNames($this->names) - ->setNotNames($this->notNames) - ->setContains($this->contains) - ->setNotContains($this->notContains) - ->setSizes($this->sizes) - ->setDates($this->dates) - ->setFilters($this->filters) - ->setSort($this->sort) - ->setPath($this->paths) - ->setNotPath($this->notPaths) - ->ignoreUnreadableDirs($this->ignoreUnreadableDirs); - } - - /** - * Unselects all adapters. - */ - private function resetAdapterSelection() - { - $this->adapters = array_map(function (array $properties) { - $properties['selected'] = false; - - return $properties; - }, $this->adapters); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php b/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php deleted file mode 100644 index df65ac754..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Glob.php +++ /dev/null @@ -1,103 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder; - -/** - * Glob matches globbing patterns against text. - * - * if match_glob("foo.*", "foo.bar") echo "matched\n"; - * - * // prints foo.bar and foo.baz - * $regex = glob_to_regex("foo.*"); - * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t) - * { - * if (/$regex/) echo "matched: $car\n"; - * } - * - * Glob implements glob(3) style matching that can be used to match - * against text, rather than fetching names from a filesystem. - * - * Based on the Perl Text::Glob module. - * - * @author Fabien Potencier PHP port - * @author Richard Clamp Perl version - * @copyright 2004-2005 Fabien Potencier - * @copyright 2002 Richard Clamp - */ -class Glob -{ - /** - * Returns a regexp which is the equivalent of the glob pattern. - * - * @param string $glob The glob pattern - * @param bool $strictLeadingDot - * @param bool $strictWildcardSlash - * - * @return string regex The regexp - */ - public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true) - { - $firstByte = true; - $escaping = false; - $inCurlies = 0; - $regex = ''; - $sizeGlob = strlen($glob); - for ($i = 0; $i < $sizeGlob; $i++) { - $car = $glob[$i]; - if ($firstByte) { - if ($strictLeadingDot && '.' !== $car) { - $regex .= '(?=[^\.])'; - } - - $firstByte = false; - } - - if ('/' === $car) { - $firstByte = true; - } - - if ('.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { - $regex .= "\\$car"; - } elseif ('*' === $car) { - $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); - } elseif ('?' === $car) { - $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); - } elseif ('{' === $car) { - $regex .= $escaping ? '\\{' : '('; - if (!$escaping) { - ++$inCurlies; - } - } elseif ('}' === $car && $inCurlies) { - $regex .= $escaping ? '}' : ')'; - if (!$escaping) { - --$inCurlies; - } - } elseif (',' === $car && $inCurlies) { - $regex .= $escaping ? ',' : '|'; - } elseif ('\\' === $car) { - if ($escaping) { - $regex .= '\\\\'; - $escaping = false; - } else { - $escaping = true; - } - - continue; - } else { - $regex .= $car; - } - $escaping = false; - } - - return '#^'.$regex.'$#'; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php deleted file mode 100644 index 36d79dbc9..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/CustomFilterIterator.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * CustomFilterIterator filters files by applying anonymous functions. - * - * The anonymous function receives a \SplFileInfo and must return false - * to remove files. - * - * @author Fabien Potencier - */ -class CustomFilterIterator extends FilterIterator -{ - private $filters = array(); - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param array $filters An array of PHP callbacks - * - * @throws \InvalidArgumentException - */ - public function __construct(\Iterator $iterator, array $filters) - { - foreach ($filters as $filter) { - if (!is_callable($filter)) { - throw new \InvalidArgumentException('Invalid PHP callback.'); - } - } - $this->filters = $filters; - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $fileinfo = $this->current(); - - foreach ($this->filters as $filter) { - if (false === call_user_func($filter, $fileinfo)) { - return false; - } - } - - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php deleted file mode 100644 index bad682ee5..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DateRangeFilterIterator.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\Comparator\DateComparator; - -/** - * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates). - * - * @author Fabien Potencier - */ -class DateRangeFilterIterator extends FilterIterator -{ - private $comparators = array(); - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param DateComparator[] $comparators An array of DateComparator instances - */ - public function __construct(\Iterator $iterator, array $comparators) - { - $this->comparators = $comparators; - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $fileinfo = $this->current(); - - if (!$fileinfo->isFile()) { - return true; - } - - $filedate = $fileinfo->getMTime(); - foreach ($this->comparators as $compare) { - if (!$compare->test($filedate)) { - return false; - } - } - - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php deleted file mode 100644 index c1c5ce899..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/DepthRangeFilterIterator.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * DepthRangeFilterIterator limits the directory depth. - * - * @author Fabien Potencier - */ -class DepthRangeFilterIterator extends FilterIterator -{ - private $minDepth = 0; - - /** - * Constructor. - * - * @param \RecursiveIteratorIterator $iterator The Iterator to filter - * @param int $minDepth The min depth - * @param int $maxDepth The max depth - */ - public function __construct(\RecursiveIteratorIterator $iterator, $minDepth = 0, $maxDepth = PHP_INT_MAX) - { - $this->minDepth = $minDepth; - $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - return $this->getInnerIterator()->getDepth() >= $this->minDepth; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php deleted file mode 100644 index b2be58ea7..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * ExcludeDirectoryFilterIterator filters out directories. - * - * @author Fabien Potencier - */ -class ExcludeDirectoryFilterIterator extends FilterIterator -{ - private $patterns = array(); - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param array $directories An array of directories to exclude - */ - public function __construct(\Iterator $iterator, array $directories) - { - foreach ($directories as $directory) { - $this->patterns[] = '#(^|/)'.preg_quote($directory, '#').'(/|$)#'; - } - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); - $path = strtr($path, '\\', '/'); - foreach ($this->patterns as $pattern) { - if (preg_match($pattern, $path)) { - return false; - } - } - - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php deleted file mode 100644 index 48634f27d..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilePathsIterator.php +++ /dev/null @@ -1,131 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\SplFileInfo; - -/** - * Iterate over shell command result. - * - * @author Jean-François Simon - */ -class FilePathsIterator extends \ArrayIterator -{ - /** - * @var string - */ - private $baseDir; - - /** - * @var int - */ - private $baseDirLength; - - /** - * @var string - */ - private $subPath; - - /** - * @var string - */ - private $subPathname; - - /** - * @var SplFileInfo - */ - private $current; - - /** - * @param array $paths List of paths returned by shell command - * @param string $baseDir Base dir for relative path building - */ - public function __construct(array $paths, $baseDir) - { - $this->baseDir = $baseDir; - $this->baseDirLength = strlen($baseDir); - - parent::__construct($paths); - } - - /** - * @param string $name - * @param array $arguments - * - * @return mixed - */ - public function __call($name, array $arguments) - { - return call_user_func_array(array($this->current(), $name), $arguments); - } - - /** - * Return an instance of SplFileInfo with support for relative paths. - * - * @return SplFileInfo File information - */ - public function current() - { - return $this->current; - } - - /** - * @return string - */ - public function key() - { - return $this->current->getPathname(); - } - - public function next() - { - parent::next(); - $this->buildProperties(); - } - - public function rewind() - { - parent::rewind(); - $this->buildProperties(); - } - - /** - * @return string - */ - public function getSubPath() - { - return $this->subPath; - } - - /** - * @return string - */ - public function getSubPathname() - { - return $this->subPathname; - } - - private function buildProperties() - { - $absolutePath = parent::current(); - - if ($this->baseDir === substr($absolutePath, 0, $this->baseDirLength)) { - $this->subPathname = ltrim(substr($absolutePath, $this->baseDirLength), '/\\'); - $dir = dirname($this->subPathname); - $this->subPath = '.' === $dir ? '' : $dir; - } else { - $this->subPath = $this->subPathname = ''; - } - - $this->current = new SplFileInfo(parent::current(), $this->subPath, $this->subPathname); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php deleted file mode 100644 index 985475e59..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FileTypeFilterIterator.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * FileTypeFilterIterator only keeps files, directories, or both. - * - * @author Fabien Potencier - */ -class FileTypeFilterIterator extends FilterIterator -{ - const ONLY_FILES = 1; - const ONLY_DIRECTORIES = 2; - - private $mode; - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) - */ - public function __construct(\Iterator $iterator, $mode) - { - $this->mode = $mode; - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $fileinfo = $this->current(); - if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { - return false; - } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { - return false; - } - - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php deleted file mode 100644 index 9ea333013..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilecontentFilterIterator.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). - * - * @author Fabien Potencier - * @author Włodzimierz Gajda - */ -class FilecontentFilterIterator extends MultiplePcreFilterIterator -{ - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - if (!$this->matchRegexps && !$this->noMatchRegexps) { - return true; - } - - $fileinfo = $this->current(); - - if ($fileinfo->isDir() || !$fileinfo->isReadable()) { - return false; - } - - $content = $fileinfo->getContents(); - if (!$content) { - return false; - } - - // should at least not match one rule to exclude - foreach ($this->noMatchRegexps as $regex) { - if (preg_match($regex, $content)) { - return false; - } - } - - // should at least match one rule - $match = true; - if ($this->matchRegexps) { - $match = false; - foreach ($this->matchRegexps as $regex) { - if (preg_match($regex, $content)) { - return true; - } - } - } - - return $match; - } - - /** - * Converts string to regexp if necessary. - * - * @param string $str Pattern: string or regexp - * - * @return string regexp corresponding to a given string or regexp - */ - protected function toRegex($str) - { - return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php deleted file mode 100644 index c4e0ccd8e..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilenameFilterIterator.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\Expression\Expression; - -/** - * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string). - * - * @author Fabien Potencier - */ -class FilenameFilterIterator extends MultiplePcreFilterIterator -{ - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $filename = $this->current()->getFilename(); - - // should at least not match one rule to exclude - foreach ($this->noMatchRegexps as $regex) { - if (preg_match($regex, $filename)) { - return false; - } - } - - // should at least match one rule - $match = true; - if ($this->matchRegexps) { - $match = false; - foreach ($this->matchRegexps as $regex) { - if (preg_match($regex, $filename)) { - return true; - } - } - } - - return $match; - } - - /** - * Converts glob to regexp. - * - * PCRE patterns are left unchanged. - * Glob strings are transformed with Glob::toRegex(). - * - * @param string $str Pattern: glob or regexp - * - * @return string regexp corresponding to a given glob or regexp - */ - protected function toRegex($str) - { - return Expression::create($str)->getRegex()->render(); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php deleted file mode 100644 index f4da44c4c..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/FilterIterator.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * This iterator just overrides the rewind method in order to correct a PHP bug. - * - * @see https://bugs.php.net/bug.php?id=49104 - * - * @author Alex Bogomazov - */ -abstract class FilterIterator extends \FilterIterator -{ - /** - * This is a workaround for the problem with \FilterIterator leaving inner \FilesystemIterator in wrong state after - * rewind in some cases. - * - * @see FilterIterator::rewind() - */ - public function rewind() - { - $iterator = $this; - while ($iterator instanceof \OuterIterator) { - $innerIterator = $iterator->getInnerIterator(); - - if ($innerIterator instanceof RecursiveDirectoryIterator) { - if ($innerIterator->isRewindable()) { - $innerIterator->next(); - $innerIterator->rewind(); - } - } elseif ($iterator->getInnerIterator() instanceof \FilesystemIterator) { - $iterator->getInnerIterator()->next(); - $iterator->getInnerIterator()->rewind(); - } - $iterator = $iterator->getInnerIterator(); - } - - parent::rewind(); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php deleted file mode 100644 index d7efc9090..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/MultiplePcreFilterIterator.php +++ /dev/null @@ -1,66 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\Expression\Expression; - -/** - * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings). - * - * @author Fabien Potencier - */ -abstract class MultiplePcreFilterIterator extends FilterIterator -{ - protected $matchRegexps = array(); - protected $noMatchRegexps = array(); - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param array $matchPatterns An array of patterns that need to match - * @param array $noMatchPatterns An array of patterns that need to not match - */ - public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) - { - foreach ($matchPatterns as $pattern) { - $this->matchRegexps[] = $this->toRegex($pattern); - } - - foreach ($noMatchPatterns as $pattern) { - $this->noMatchRegexps[] = $this->toRegex($pattern); - } - - parent::__construct($iterator); - } - - /** - * Checks whether the string is a regex. - * - * @param string $str - * - * @return bool Whether the given string is a regex - */ - protected function isRegex($str) - { - return Expression::create($str)->isRegex(); - } - - /** - * Converts string into regexp. - * - * @param string $str Pattern - * - * @return string regexp corresponding to a given string - */ - abstract protected function toRegex($str); -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php deleted file mode 100644 index 828fa4187..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/PathFilterIterator.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * PathFilterIterator filters files by path patterns (e.g. some/special/dir). - * - * @author Fabien Potencier - * @author Włodzimierz Gajda - */ -class PathFilterIterator extends MultiplePcreFilterIterator -{ - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $filename = $this->current()->getRelativePathname(); - - if (defined('PHP_WINDOWS_VERSION_MAJOR')) { - $filename = strtr($filename, '\\', '/'); - } - - // should at least not match one rule to exclude - foreach ($this->noMatchRegexps as $regex) { - if (preg_match($regex, $filename)) { - return false; - } - } - - // should at least match one rule - $match = true; - if ($this->matchRegexps) { - $match = false; - foreach ($this->matchRegexps as $regex) { - if (preg_match($regex, $filename)) { - return true; - } - } - } - - return $match; - } - - /** - * Converts strings to regexp. - * - * PCRE patterns are left unchanged. - * - * Default conversion: - * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/' - * - * Use only / as directory separator (on Windows also). - * - * @param string $str Pattern: regexp or dirname. - * - * @return string regexp corresponding to a given string or regexp - */ - protected function toRegex($str) - { - return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php deleted file mode 100644 index 9543a3f76..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/RecursiveDirectoryIterator.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\Exception\AccessDeniedException; -use Symfony\Component\Finder\SplFileInfo; - -/** - * Extends the \RecursiveDirectoryIterator to support relative paths - * - * @author Victor Berchet - */ -class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator -{ - /** - * @var bool - */ - private $ignoreUnreadableDirs; - - /** - * @var bool - */ - private $rewindable; - - /** - * Constructor. - * - * @param string $path - * @param int $flags - * @param bool $ignoreUnreadableDirs - * - * @throws \RuntimeException - */ - public function __construct($path, $flags, $ignoreUnreadableDirs = false) - { - if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { - throw new \RuntimeException('This iterator only support returning current as fileinfo.'); - } - - parent::__construct($path, $flags); - $this->ignoreUnreadableDirs = $ignoreUnreadableDirs; - } - - /** - * Return an instance of SplFileInfo with support for relative paths - * - * @return SplFileInfo File information - */ - public function current() - { - return new SplFileInfo(parent::current()->getPathname(), $this->getSubPath(), $this->getSubPathname()); - } - - /** - * @return \RecursiveIterator - * - * @throws AccessDeniedException - */ - public function getChildren() - { - try { - $children = parent::getChildren(); - - if ($children instanceof self) { - // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore - $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs; - } - - return $children; - } catch (\UnexpectedValueException $e) { - if ($this->ignoreUnreadableDirs) { - // If directory is unreadable and finder is set to ignore it, a fake empty content is returned. - return new \RecursiveArrayIterator(array()); - } else { - throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); - } - } - } - - /** - * Do nothing for non rewindable stream - */ - public function rewind() - { - if (false === $this->isRewindable()) { - return; - } - - // @see https://bugs.php.net/bug.php?id=49104 - parent::next(); - - parent::rewind(); - } - - /** - * Checks if the stream is rewindable. - * - * @return bool true when the stream is rewindable, false otherwise - */ - public function isRewindable() - { - if (null !== $this->rewindable) { - return $this->rewindable; - } - - if (false !== $stream = @opendir($this->getPath())) { - $infos = stream_get_meta_data($stream); - closedir($stream); - - if ($infos['seekable']) { - return $this->rewindable = true; - } - } - - return $this->rewindable = false; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php deleted file mode 100644 index b345a1692..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SizeRangeFilterIterator.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -use Symfony\Component\Finder\Comparator\NumberComparator; - -/** - * SizeRangeFilterIterator filters out files that are not in the given size range. - * - * @author Fabien Potencier - */ -class SizeRangeFilterIterator extends FilterIterator -{ - private $comparators = array(); - - /** - * Constructor. - * - * @param \Iterator $iterator The Iterator to filter - * @param NumberComparator[] $comparators An array of NumberComparator instances - */ - public function __construct(\Iterator $iterator, array $comparators) - { - $this->comparators = $comparators; - - parent::__construct($iterator); - } - - /** - * Filters the iterator values. - * - * @return bool true if the value should be kept, false otherwise - */ - public function accept() - { - $fileinfo = $this->current(); - if (!$fileinfo->isFile()) { - return true; - } - - $filesize = $fileinfo->getSize(); - foreach ($this->comparators as $compare) { - if (!$compare->test($filesize)) { - return false; - } - } - - return true; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php b/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php deleted file mode 100644 index 3bf5034d1..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Iterator/SortableIterator.php +++ /dev/null @@ -1,82 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Iterator; - -/** - * SortableIterator applies a sort on a given Iterator. - * - * @author Fabien Potencier - */ -class SortableIterator implements \IteratorAggregate -{ - const SORT_BY_NAME = 1; - const SORT_BY_TYPE = 2; - const SORT_BY_ACCESSED_TIME = 3; - const SORT_BY_CHANGED_TIME = 4; - const SORT_BY_MODIFIED_TIME = 5; - - private $iterator; - private $sort; - - /** - * Constructor. - * - * @param \Traversable $iterator The Iterator to filter - * @param int|callback $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) - * - * @throws \InvalidArgumentException - */ - public function __construct(\Traversable $iterator, $sort) - { - $this->iterator = $iterator; - - if (self::SORT_BY_NAME === $sort) { - $this->sort = function ($a, $b) { - return strcmp($a->getRealpath(), $b->getRealpath()); - }; - } elseif (self::SORT_BY_TYPE === $sort) { - $this->sort = function ($a, $b) { - if ($a->isDir() && $b->isFile()) { - return -1; - } elseif ($a->isFile() && $b->isDir()) { - return 1; - } - - return strcmp($a->getRealpath(), $b->getRealpath()); - }; - } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { - $this->sort = function ($a, $b) { - return ($a->getATime() > $b->getATime()); - }; - } elseif (self::SORT_BY_CHANGED_TIME === $sort) { - $this->sort = function ($a, $b) { - return ($a->getCTime() > $b->getCTime()); - }; - } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { - $this->sort = function ($a, $b) { - return ($a->getMTime() > $b->getMTime()); - }; - } elseif (is_callable($sort)) { - $this->sort = $sort; - } else { - throw new \InvalidArgumentException('The SortableIterator takes a PHP callback or a valid built-in sort algorithm as an argument.'); - } - } - - public function getIterator() - { - $array = iterator_to_array($this->iterator, true); - uasort($array, $this->sort); - - return new \ArrayIterator($array); - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE b/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE deleted file mode 100644 index 0b3292cf9..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2014 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/README.md b/core/lib/symfony/finder/Symfony/Component/Finder/README.md deleted file mode 100644 index 8494531d2..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/README.md +++ /dev/null @@ -1,41 +0,0 @@ -Finder Component -================ - -Finder finds files and directories via an intuitive fluent interface. - - use Symfony\Component\Finder\Finder; - - $finder = new Finder(); - - $iterator = $finder - ->files() - ->name('*.php') - ->depth(0) - ->size('>= 1K') - ->in(__DIR__); - - foreach ($iterator as $file) { - print $file->getRealpath()."\n"; - } - -But you can also use it to find files stored remotely like in this example where -we are looking for files on Amazon S3: - - $s3 = new \Zend_Service_Amazon_S3($key, $secret); - $s3->registerStreamWrapper("s3"); - - $finder = new Finder(); - $finder->name('photos*')->size('< 100K')->date('since 1 hour ago'); - foreach ($finder->in('s3://bucket-name') as $file) { - print $file->getFilename()."\n"; - } - -Resources ---------- - -You can run the unit tests with the following command: - - $ cd path/to/Symfony/Component/Finder/ - $ composer.phar install - $ phpunit - diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php deleted file mode 100644 index 78824d0df..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Command.php +++ /dev/null @@ -1,294 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Shell; - -/** - * @author Jean-François Simon - */ -class Command -{ - /** - * @var Command|null - */ - private $parent; - - /** - * @var array - */ - private $bits = array(); - - /** - * @var array - */ - private $labels = array(); - - /** - * @var \Closure|null - */ - private $errorHandler; - - /** - * Constructor. - * - * @param Command|null $parent Parent command - */ - public function __construct(Command $parent = null) - { - $this->parent = $parent; - } - - /** - * Returns command as string. - * - * @return string - */ - public function __toString() - { - return $this->join(); - } - - /** - * Creates a new Command instance. - * - * @param Command|null $parent Parent command - * - * @return Command New Command instance - */ - public static function create(Command $parent = null) - { - return new self($parent); - } - - /** - * Escapes special chars from input. - * - * @param string $input A string to escape - * - * @return string The escaped string - */ - public static function escape($input) - { - return escapeshellcmd($input); - } - - /** - * Quotes input. - * - * @param string $input An argument string - * - * @return string The quoted string - */ - public static function quote($input) - { - return escapeshellarg($input); - } - - /** - * Appends a string or a Command instance. - * - * @param string|Command $bit - * - * @return Command The current Command instance - */ - public function add($bit) - { - $this->bits[] = $bit; - - return $this; - } - - /** - * Prepends a string or a command instance. - * - * @param string|Command $bit - * - * @return Command The current Command instance - */ - public function top($bit) - { - array_unshift($this->bits, $bit); - - foreach ($this->labels as $label => $index) { - $this->labels[$label] += 1; - } - - return $this; - } - - /** - * Appends an argument, will be quoted. - * - * @param string $arg - * - * @return Command The current Command instance - */ - public function arg($arg) - { - $this->bits[] = self::quote($arg); - - return $this; - } - - /** - * Appends escaped special command chars. - * - * @param string $esc - * - * @return Command The current Command instance - */ - public function cmd($esc) - { - $this->bits[] = self::escape($esc); - - return $this; - } - - /** - * Inserts a labeled command to feed later. - * - * @param string $label The unique label - * - * @return Command The current Command instance - * - * @throws \RuntimeException If label already exists - */ - public function ins($label) - { - if (isset($this->labels[$label])) { - throw new \RuntimeException(sprintf('Label "%s" already exists.', $label)); - } - - $this->bits[] = self::create($this); - $this->labels[$label] = count($this->bits)-1; - - return $this->bits[$this->labels[$label]]; - } - - /** - * Retrieves a previously labeled command. - * - * @param string $label - * - * @return Command The labeled command - * - * @throws \RuntimeException - */ - public function get($label) - { - if (!isset($this->labels[$label])) { - throw new \RuntimeException(sprintf('Label "%s" does not exist.', $label)); - } - - return $this->bits[$this->labels[$label]]; - } - - /** - * Returns parent command (if any). - * - * @return Command Parent command - * - * @throws \RuntimeException If command has no parent - */ - public function end() - { - if (null === $this->parent) { - throw new \RuntimeException('Calling end on root command doesn\'t make sense.'); - } - - return $this->parent; - } - - /** - * Counts bits stored in command. - * - * @return int The bits count - */ - public function length() - { - return count($this->bits); - } - - /** - * @param \Closure $errorHandler - * - * @return Command - */ - public function setErrorHandler(\Closure $errorHandler) - { - $this->errorHandler = $errorHandler; - - return $this; - } - - /** - * @return \Closure|null - */ - public function getErrorHandler() - { - return $this->errorHandler; - } - - /** - * Executes current command. - * - * @return array The command result - * - * @throws \RuntimeException - */ - public function execute() - { - if (null === $this->errorHandler) { - exec($this->join(), $output); - } else { - $process = proc_open($this->join(), array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $pipes); - $output = preg_split('~(\r\n|\r|\n)~', stream_get_contents($pipes[1]), -1, PREG_SPLIT_NO_EMPTY); - - if ($error = stream_get_contents($pipes[2])) { - call_user_func($this->errorHandler, $error); - } - - proc_close($process); - } - - return $output ?: array(); - } - - /** - * Joins bits. - * - * @return string - */ - public function join() - { - return implode(' ', array_filter( - array_map(function ($bit) { - return $bit instanceof Command ? $bit->join() : ($bit ?: null); - }, $this->bits), - function ($bit) { return null !== $bit; } - )); - } - - /** - * Insert a string or a Command instance before the bit at given position $index (index starts from 0). - * - * @param string|Command $bit - * @param int $index - * - * @return Command The current Command instance - */ - public function addAtIndex($bit, $index) - { - array_splice($this->bits, $index, 0, $bit); - - return $this; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php b/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php deleted file mode 100644 index 8586cbc3e..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/Shell/Shell.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder\Shell; - -/** - * @author Jean-François Simon - */ -class Shell -{ - const TYPE_UNIX = 1; - const TYPE_DARWIN = 2; - const TYPE_CYGWIN = 3; - const TYPE_WINDOWS = 4; - const TYPE_BSD = 5; - - /** - * @var string|null - */ - private $type; - - /** - * Returns guessed OS type. - * - * @return int - */ - public function getType() - { - if (null === $this->type) { - $this->type = $this->guessType(); - } - - return $this->type; - } - - /** - * Tests if a command is available. - * - * @param string $command - * - * @return bool - */ - public function testCommand($command) - { - if (self::TYPE_WINDOWS === $this->type) { - // todo: find a way to test if Windows command exists - return false; - } - - if (!function_exists('exec')) { - return false; - } - - // todo: find a better way (command could not be available) - exec('command -v '.$command, $output, $code); - - return 0 === $code && count($output) > 0; - } - - /** - * Guesses OS type. - * - * @return int - */ - private function guessType() - { - $os = strtolower(PHP_OS); - - if (false !== strpos($os, 'cygwin')) { - return self::TYPE_CYGWIN; - } - - if (false !== strpos($os, 'darwin')) { - return self::TYPE_DARWIN; - } - - if (false !== strpos($os, 'bsd')) { - return self::TYPE_BSD; - } - - if (0 === strpos($os, 'win')) { - return self::TYPE_WINDOWS; - } - - return self::TYPE_UNIX; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php b/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php deleted file mode 100644 index d911fe06e..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/SplFileInfo.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Finder; - -/** - * Extends \SplFileInfo to support relative paths - * - * @author Fabien Potencier - */ -class SplFileInfo extends \SplFileInfo -{ - private $relativePath; - private $relativePathname; - - /** - * Constructor - * - * @param string $file The file name - * @param string $relativePath The relative path - * @param string $relativePathname The relative path name - */ - public function __construct($file, $relativePath, $relativePathname) - { - parent::__construct($file); - $this->relativePath = $relativePath; - $this->relativePathname = $relativePathname; - } - - /** - * Returns the relative path - * - * @return string the relative path - */ - public function getRelativePath() - { - return $this->relativePath; - } - - /** - * Returns the relative path name - * - * @return string the relative path name - */ - public function getRelativePathname() - { - return $this->relativePathname; - } - - /** - * Returns the contents of the file - * - * @return string the contents of the file - * - * @throws \RuntimeException - */ - public function getContents() - { - $level = error_reporting(0); - $content = file_get_contents($this->getPathname()); - error_reporting($level); - if (false === $content) { - $error = error_get_last(); - throw new \RuntimeException($error['message']); - } - - return $content; - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/composer.json b/core/lib/symfony/finder/Symfony/Component/Finder/composer.json deleted file mode 100644 index 7de47137f..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "symfony/finder", - "type": "library", - "description": "Symfony Finder Component", - "keywords": [], - "homepage": "http://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.3.3" - }, - "autoload": { - "psr-0": { "Symfony\\Component\\Finder\\": "" } - }, - "target-dir": "Symfony/Component/Finder", - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - } -} diff --git a/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist b/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist deleted file mode 100644 index 23272235b..000000000 --- a/core/lib/symfony/finder/Symfony/Component/Finder/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - ./vendor - - - - diff --git a/core/lib/symfony/process/Symfony/Component/Process/.gitignore b/core/lib/symfony/process/Symfony/Component/Process/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md b/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md deleted file mode 100644 index 3d13b99cc..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -CHANGELOG -========= - -2.4.0 ------ - - * added the ability to define an idle timeout - -2.3.0 ------ - - * added ProcessUtils::escapeArgument() to fix the bug in escapeshellarg() function on Windows - * added Process::signal() - * added Process::getPid() - * added support for a TTY mode - -2.2.0 ------ - - * added ProcessBuilder::setArguments() to reset the arguments on a builder - * added a way to retrieve the standard and error output incrementally - * added Process:restart() - -2.1.0 ------ - - * added support for non-blocking processes (start(), wait(), isRunning(), stop()) - * enhanced Windows compatibility - * added Process::getExitCodeText() that returns a string representation for - the exit code returned by the process - * added ProcessBuilder diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php deleted file mode 100644 index 75c1c9e5d..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/ExceptionInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -/** - * Marker Interface for the Process Component. - * - * @author Johannes M. Schmitt - */ -interface ExceptionInterface -{ -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php deleted file mode 100644 index 926ee2118..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -/** - * InvalidArgumentException for the Process Component. - * - * @author Romain Neutron - */ -class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface -{ -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php deleted file mode 100644 index be3d490dd..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/LogicException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -/** - * LogicException for the Process Component. - * - * @author Romain Neutron - */ -class LogicException extends \LogicException implements ExceptionInterface -{ -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php deleted file mode 100644 index 890935933..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessFailedException.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -use Symfony\Component\Process\Process; - -/** - * Exception for failed processes. - * - * @author Johannes M. Schmitt - */ -class ProcessFailedException extends RuntimeException -{ - private $process; - - public function __construct(Process $process) - { - if ($process->isSuccessful()) { - throw new InvalidArgumentException('Expected a failed process, but the given process was successful.'); - } - - parent::__construct( - sprintf( - 'The command "%s" failed.'."\nExit Code: %s(%s)\n\nOutput:\n================\n%s\n\nError Output:\n================\n%s", - $process->getCommandLine(), - $process->getExitCode(), - $process->getExitCodeText(), - $process->getOutput(), - $process->getErrorOutput() - ) - ); - - $this->process = $process; - } - - public function getProcess() - { - return $this->process; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php deleted file mode 100644 index d45114696..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/ProcessTimedOutException.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -use Symfony\Component\Process\Process; - -/** - * Exception that is thrown when a process times out. - * - * @author Johannes M. Schmitt - */ -class ProcessTimedOutException extends RuntimeException -{ - const TYPE_GENERAL = 1; - const TYPE_IDLE = 2; - - private $process; - private $timeoutType; - - public function __construct(Process $process, $timeoutType) - { - $this->process = $process; - $this->timeoutType = $timeoutType; - - parent::__construct(sprintf( - 'The process "%s" exceeded the timeout of %s seconds.', - $process->getCommandLine(), - $this->getExceededTimeout() - )); - } - - public function getProcess() - { - return $this->process; - } - - public function isGeneralTimeout() - { - return $this->timeoutType === self::TYPE_GENERAL; - } - - public function isIdleTimeout() - { - return $this->timeoutType === self::TYPE_IDLE; - } - - public function getExceededTimeout() - { - switch ($this->timeoutType) { - case self::TYPE_GENERAL: - return $this->process->getTimeout(); - - case self::TYPE_IDLE: - return $this->process->getIdleTimeout(); - - default: - throw new \LogicException(sprintf('Unknown timeout type "%d".', $this->timeoutType)); - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php b/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php deleted file mode 100644 index adead2536..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Exception/RuntimeException.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Exception; - -/** - * RuntimeException for the Process Component. - * - * @author Johannes M. Schmitt - */ -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php b/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php deleted file mode 100644 index 5cc99c769..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/ExecutableFinder.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -/** - * Generic executable finder. - * - * @author Fabien Potencier - * @author Johannes M. Schmitt - */ -class ExecutableFinder -{ - private $suffixes = array('.exe', '.bat', '.cmd', '.com'); - - /** - * Replaces default suffixes of executable. - * - * @param array $suffixes - */ - public function setSuffixes(array $suffixes) - { - $this->suffixes = $suffixes; - } - - /** - * Adds new possible suffix to check for executable. - * - * @param string $suffix - */ - public function addSuffix($suffix) - { - $this->suffixes[] = $suffix; - } - - /** - * Finds an executable by name. - * - * @param string $name The executable name (without the extension) - * @param string $default The default to return if no executable is found - * @param array $extraDirs Additional dirs to check into - * - * @return string The executable path or default value - */ - public function find($name, $default = null, array $extraDirs = array()) - { - if (ini_get('open_basedir')) { - $searchPath = explode(PATH_SEPARATOR, getenv('open_basedir')); - $dirs = array(); - foreach ($searchPath as $path) { - if (is_dir($path)) { - $dirs[] = $path; - } else { - $file = str_replace(dirname($path), '', $path); - if ($file == $name && is_executable($path)) { - return $path; - } - } - } - } else { - $dirs = array_merge( - explode(PATH_SEPARATOR, getenv('PATH') ?: getenv('Path')), - $extraDirs - ); - } - - $suffixes = array(''); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $pathExt = getenv('PATHEXT'); - $suffixes = $pathExt ? explode(PATH_SEPARATOR, $pathExt) : $this->suffixes; - } - foreach ($suffixes as $suffix) { - foreach ($dirs as $dir) { - if (is_file($file = $dir.DIRECTORY_SEPARATOR.$name.$suffix) && (defined('PHP_WINDOWS_VERSION_BUILD') || is_executable($file))) { - return $file; - } - } - } - - return $default; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/LICENSE b/core/lib/symfony/process/Symfony/Component/Process/LICENSE deleted file mode 100644 index 0b3292cf9..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2014 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php b/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php deleted file mode 100644 index 9001f4109..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/PhpExecutableFinder.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -/** - * An executable finder specifically designed for the PHP executable. - * - * @author Fabien Potencier - * @author Johannes M. Schmitt - */ -class PhpExecutableFinder -{ - private $executableFinder; - - public function __construct() - { - $this->executableFinder = new ExecutableFinder(); - } - - /** - * Finds The PHP executable. - * - * @return string|false The PHP executable path or false if it cannot be found - */ - public function find() - { - // HHVM support - if (defined('HHVM_VERSION') && false !== $hhvm = getenv('PHP_BINARY')) { - return $hhvm; - } - - // PHP_BINARY return the current sapi executable - if (defined('PHP_BINARY') && PHP_BINARY && in_array(PHP_SAPI, array('cli', 'cli-server')) && is_file(PHP_BINARY)) { - return PHP_BINARY; - } - - if ($php = getenv('PHP_PATH')) { - if (!is_executable($php)) { - return false; - } - - return $php; - } - - if ($php = getenv('PHP_PEAR_PHP_BIN')) { - if (is_executable($php)) { - return $php; - } - } - - $dirs = array(PHP_BINDIR); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $dirs[] = 'C:\xampp\php\\'; - } - - return $this->executableFinder->find('php', false, $dirs); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php b/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php deleted file mode 100644 index 93948e1db..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/PhpProcess.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -use Symfony\Component\Process\Exception\RuntimeException; - -/** - * PhpProcess runs a PHP script in an independent process. - * - * $p = new PhpProcess(''); - * $p->run(); - * print $p->getOutput()."\n"; - * - * @author Fabien Potencier - * - * @api - */ -class PhpProcess extends Process -{ - private $executableFinder; - - /** - * Constructor. - * - * @param string $script The PHP script to run (as a string) - * @param string $cwd The working directory - * @param array $env The environment variables - * @param int $timeout The timeout in seconds - * @param array $options An array of options for proc_open - * - * @api - */ - public function __construct($script, $cwd = null, array $env = array(), $timeout = 60, array $options = array()) - { - parent::__construct(null, $cwd, $env, $script, $timeout, $options); - - $this->executableFinder = new PhpExecutableFinder(); - } - - /** - * Sets the path to the PHP binary to use. - * - * @api - */ - public function setPhpBinary($php) - { - $this->setCommandLine($php); - } - - /** - * {@inheritdoc} - */ - public function start($callback = null) - { - if (null === $this->getCommandLine()) { - if (false === $php = $this->executableFinder->find()) { - throw new RuntimeException('Unable to find the PHP executable.'); - } - $this->setCommandLine($php); - } - - parent::start($callback); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Process.php b/core/lib/symfony/process/Symfony/Component/Process/Process.php deleted file mode 100644 index c49d7a56a..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Process.php +++ /dev/null @@ -1,1312 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -use Symfony\Component\Process\Exception\InvalidArgumentException; -use Symfony\Component\Process\Exception\LogicException; -use Symfony\Component\Process\Exception\ProcessTimedOutException; -use Symfony\Component\Process\Exception\RuntimeException; - -/** - * Process is a thin wrapper around proc_* functions to easily - * start independent PHP processes. - * - * @author Fabien Potencier - * - * @api - */ -class Process -{ - const ERR = 'err'; - const OUT = 'out'; - - const STATUS_READY = 'ready'; - const STATUS_STARTED = 'started'; - const STATUS_TERMINATED = 'terminated'; - - const STDIN = 0; - const STDOUT = 1; - const STDERR = 2; - - // Timeout Precision in seconds. - const TIMEOUT_PRECISION = 0.2; - - private $callback; - private $commandline; - private $cwd; - private $env; - private $stdin; - private $starttime; - private $lastOutputTime; - private $timeout; - private $idleTimeout; - private $options; - private $exitcode; - private $fallbackExitcode; - private $processInformation; - private $stdout; - private $stderr; - private $enhanceWindowsCompatibility = true; - private $enhanceSigchildCompatibility; - private $process; - private $status = self::STATUS_READY; - private $incrementalOutputOffset = 0; - private $incrementalErrorOutputOffset = 0; - private $tty; - - private $useFileHandles = false; - /** @var ProcessPipes */ - private $processPipes; - - private static $sigchild; - - /** - * Exit codes translation table. - * - * User-defined errors must use exit codes in the 64-113 range. - * - * @var array - */ - public static $exitCodes = array( - 0 => 'OK', - 1 => 'General error', - 2 => 'Misuse of shell builtins', - - 126 => 'Invoked command cannot execute', - 127 => 'Command not found', - 128 => 'Invalid exit argument', - - // signals - 129 => 'Hangup', - 130 => 'Interrupt', - 131 => 'Quit and dump core', - 132 => 'Illegal instruction', - 133 => 'Trace/breakpoint trap', - 134 => 'Process aborted', - 135 => 'Bus error: "access to undefined portion of memory object"', - 136 => 'Floating point exception: "erroneous arithmetic operation"', - 137 => 'Kill (terminate immediately)', - 138 => 'User-defined 1', - 139 => 'Segmentation violation', - 140 => 'User-defined 2', - 141 => 'Write to pipe with no one reading', - 142 => 'Signal raised by alarm', - 143 => 'Termination (request to terminate)', - // 144 - not defined - 145 => 'Child process terminated, stopped (or continued*)', - 146 => 'Continue if stopped', - 147 => 'Stop executing temporarily', - 148 => 'Terminal stop signal', - 149 => 'Background process attempting to read from tty ("in")', - 150 => 'Background process attempting to write to tty ("out")', - 151 => 'Urgent data available on socket', - 152 => 'CPU time limit exceeded', - 153 => 'File size limit exceeded', - 154 => 'Signal raised by timer counting virtual time: "virtual timer expired"', - 155 => 'Profiling timer expired', - // 156 - not defined - 157 => 'Pollable event', - // 158 - not defined - 159 => 'Bad syscall', - ); - - /** - * Constructor. - * - * @param string $commandline The command line to run - * @param string|null $cwd The working directory or null to use the working dir of the current PHP process - * @param array|null $env The environment variables or null to inherit - * @param string|null $stdin The STDIN content - * @param int|float|null $timeout The timeout in seconds or null to disable - * @param array $options An array of options for proc_open - * - * @throws RuntimeException When proc_open is not installed - * - * @api - */ - public function __construct($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) - { - if (!function_exists('proc_open')) { - throw new RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.'); - } - - $this->commandline = $commandline; - $this->cwd = $cwd; - - // on Windows, if the cwd changed via chdir(), proc_open defaults to the dir where PHP was started - // on Gnu/Linux, PHP builds with --enable-maintainer-zts are also affected - // @see : https://bugs.php.net/bug.php?id=51800 - // @see : https://bugs.php.net/bug.php?id=50524 - if (null === $this->cwd && (defined('ZEND_THREAD_SAFE') || defined('PHP_WINDOWS_VERSION_BUILD'))) { - $this->cwd = getcwd(); - } - if (null !== $env) { - $this->setEnv($env); - } - - $this->stdin = $stdin; - $this->setTimeout($timeout); - $this->useFileHandles = defined('PHP_WINDOWS_VERSION_BUILD'); - $this->enhanceSigchildCompatibility = !defined('PHP_WINDOWS_VERSION_BUILD') && $this->isSigchildEnabled(); - $this->options = array_replace(array('suppress_errors' => true, 'binary_pipes' => true), $options); - } - - public function __destruct() - { - // stop() will check if we have a process running. - $this->stop(); - } - - public function __clone() - { - $this->resetProcessData(); - } - - /** - * Runs the process. - * - * The callback receives the type of output (out or err) and - * some bytes from the output in real-time. It allows to have feedback - * from the independent process during execution. - * - * The STDOUT and STDERR are also available after the process is finished - * via the getOutput() and getErrorOutput() methods. - * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR - * - * @return int The exit status code - * - * @throws RuntimeException When process can't be launched - * @throws RuntimeException When process stopped after receiving signal - * - * @api - */ - public function run($callback = null) - { - $this->start($callback); - - return $this->wait(); - } - - /** - * Starts the process and returns after sending the STDIN. - * - * This method blocks until all STDIN data is sent to the process then it - * returns while the process runs in the background. - * - * The termination of the process can be awaited with wait(). - * - * The callback receives the type of output (out or err) and some bytes from - * the output in real-time while writing the standard input to the process. - * It allows to have feedback from the independent process during execution. - * If there is no callback passed, the wait() method can be called - * with true as a second parameter then the callback will get all data occurred - * in (and since) the start call. - * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR - * - * @return Process The process itself - * - * @throws RuntimeException When process can't be launched - * @throws RuntimeException When process is already running - */ - public function start($callback = null) - { - if ($this->isRunning()) { - throw new RuntimeException('Process is already running'); - } - - $this->resetProcessData(); - $this->starttime = $this->lastOutputTime = microtime(true); - $this->callback = $this->buildCallback($callback); - $descriptors = $this->getDescriptors(); - - $commandline = $this->commandline; - - if (defined('PHP_WINDOWS_VERSION_BUILD') && $this->enhanceWindowsCompatibility) { - $commandline = 'cmd /V:ON /E:ON /C "('.$commandline.')'; - foreach ($this->processPipes->getFiles() as $offset => $filename) { - $commandline .= ' '.$offset.'>'.ProcessUtils::escapeArgument($filename); - } - $commandline .= '"'; - - if (!isset($this->options['bypass_shell'])) { - $this->options['bypass_shell'] = true; - } - } - - $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $this->env, $this->options); - - if (!is_resource($this->process)) { - throw new RuntimeException('Unable to launch a new process.'); - } - $this->status = self::STATUS_STARTED; - - $this->processPipes->unblock(); - - if ($this->tty) { - return; - } - - $this->processPipes->write(false, $this->stdin); - $this->updateStatus(false); - $this->checkTimeout(); - } - - /** - * Restarts the process. - * - * Be warned that the process is cloned before being started. - * - * @param callable|null $callback A PHP callback to run whenever there is some - * output available on STDOUT or STDERR - * - * @return Process The new process - * - * @throws RuntimeException When process can't be launched - * @throws RuntimeException When process is already running - * - * @see start() - */ - public function restart($callback = null) - { - if ($this->isRunning()) { - throw new RuntimeException('Process is already running'); - } - - $process = clone $this; - $process->start($callback); - - return $process; - } - - /** - * Waits for the process to terminate. - * - * The callback receives the type of output (out or err) and some bytes - * from the output in real-time while writing the standard input to the process. - * It allows to have feedback from the independent process during execution. - * - * @param callable|null $callback A valid PHP callback - * - * @return int The exitcode of the process - * - * @throws RuntimeException When process timed out - * @throws RuntimeException When process stopped after receiving signal - * @throws LogicException When process is not yet started - */ - public function wait($callback = null) - { - $this->requireProcessIsStarted(__FUNCTION__); - - $this->updateStatus(false); - if (null !== $callback) { - $this->callback = $this->buildCallback($callback); - } - - do { - $this->checkTimeout(); - $running = defined('PHP_WINDOWS_VERSION_BUILD') ? $this->isRunning() : $this->processPipes->hasOpenHandles(); - $close = !defined('PHP_WINDOWS_VERSION_BUILD') || !$running;; - $this->readPipes(true, $close); - } while ($running); - - while ($this->isRunning()) { - usleep(1000); - } - - if ($this->processInformation['signaled']) { - throw new RuntimeException(sprintf('The process has been signaled with signal "%s".', $this->processInformation['termsig'])); - } - - return $this->exitcode; - } - - /** - * Returns the Pid (process identifier), if applicable. - * - * @return int|null The process id if running, null otherwise - * - * @throws RuntimeException In case --enable-sigchild is activated - */ - public function getPid() - { - if ($this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved.'); - } - - $this->updateStatus(false); - - return $this->isRunning() ? $this->processInformation['pid'] : null; - } - - /** - * Sends a POSIX signal to the process. - * - * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php) - * - * @return Process - * - * @throws LogicException In case the process is not running - * @throws RuntimeException In case --enable-sigchild is activated - * @throws RuntimeException In case of failure - */ - public function signal($signal) - { - $this->doSignal($signal, true); - - return $this; - } - - /** - * Returns the current output of the process (STDOUT). - * - * @return string The process output - * - * @throws LogicException In case the process is not started - * - * @api - */ - public function getOutput() - { - $this->requireProcessIsStarted(__FUNCTION__); - - $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); - - return $this->stdout; - } - - /** - * Returns the output incrementally. - * - * In comparison with the getOutput method which always return the whole - * output, this one returns the new output since the last call. - * - * @throws LogicException In case the process is not started - * - * @return string The process output since the last call - */ - public function getIncrementalOutput() - { - $this->requireProcessIsStarted(__FUNCTION__); - - $data = $this->getOutput(); - - $latest = substr($data, $this->incrementalOutputOffset); - $this->incrementalOutputOffset = strlen($data); - - return $latest; - } - - /** - * Clears the process output. - * - * @return Process - */ - public function clearOutput() - { - $this->stdout = ''; - $this->incrementalOutputOffset = 0; - - return $this; - } - - /** - * Returns the current error output of the process (STDERR). - * - * @return string The process error output - * - * @throws LogicException In case the process is not started - * - * @api - */ - public function getErrorOutput() - { - $this->requireProcessIsStarted(__FUNCTION__); - - $this->readPipes(false, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); - - return $this->stderr; - } - - /** - * Returns the errorOutput incrementally. - * - * In comparison with the getErrorOutput method which always return the - * whole error output, this one returns the new error output since the last - * call. - * - * @throws LogicException In case the process is not started - * - * @return string The process error output since the last call - */ - public function getIncrementalErrorOutput() - { - $this->requireProcessIsStarted(__FUNCTION__); - - $data = $this->getErrorOutput(); - - $latest = substr($data, $this->incrementalErrorOutputOffset); - $this->incrementalErrorOutputOffset = strlen($data); - - return $latest; - } - - /** - * Clears the process output. - * - * @return Process - */ - public function clearErrorOutput() - { - $this->stderr = ''; - $this->incrementalErrorOutputOffset = 0; - - return $this; - } - - /** - * Returns the exit code returned by the process. - * - * @return null|int The exit status code, null if the Process is not terminated - * - * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled - * - * @api - */ - public function getExitCode() - { - if ($this->isSigchildEnabled() && !$this->enhanceSigchildCompatibility) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method.'); - } - - $this->updateStatus(false); - - return $this->exitcode; - } - - /** - * Returns a string representation for the exit code returned by the process. - * - * This method relies on the Unix exit code status standardization - * and might not be relevant for other operating systems. - * - * @return null|string A string representation for the exit status code, null if the Process is not terminated. - * - * @throws RuntimeException In case --enable-sigchild is activated and the sigchild compatibility mode is disabled - * - * @see http://tldp.org/LDP/abs/html/exitcodes.html - * @see http://en.wikipedia.org/wiki/Unix_signal - */ - public function getExitCodeText() - { - if (null === $exitcode = $this->getExitCode()) { - return; - } - - return isset(self::$exitCodes[$exitcode]) ? self::$exitCodes[$exitcode] : 'Unknown error'; - } - - /** - * Checks if the process ended successfully. - * - * @return bool true if the process ended successfully, false otherwise - * - * @api - */ - public function isSuccessful() - { - return 0 === $this->getExitCode(); - } - - /** - * Returns true if the child process has been terminated by an uncaught signal. - * - * It always returns false on Windows. - * - * @return bool - * - * @throws RuntimeException In case --enable-sigchild is activated - * @throws LogicException In case the process is not terminated - * - * @api - */ - public function hasBeenSignaled() - { - $this->requireProcessIsTerminated(__FUNCTION__); - - if ($this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); - } - - $this->updateStatus(false); - - return $this->processInformation['signaled']; - } - - /** - * Returns the number of the signal that caused the child process to terminate its execution. - * - * It is only meaningful if hasBeenSignaled() returns true. - * - * @return int - * - * @throws RuntimeException In case --enable-sigchild is activated - * @throws LogicException In case the process is not terminated - * - * @api - */ - public function getTermSignal() - { - $this->requireProcessIsTerminated(__FUNCTION__); - - if ($this->isSigchildEnabled()) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved.'); - } - - $this->updateStatus(false); - - return $this->processInformation['termsig']; - } - - /** - * Returns true if the child process has been stopped by a signal. - * - * It always returns false on Windows. - * - * @return bool - * - * @throws LogicException In case the process is not terminated - * - * @api - */ - public function hasBeenStopped() - { - $this->requireProcessIsTerminated(__FUNCTION__); - - $this->updateStatus(false); - - return $this->processInformation['stopped']; - } - - /** - * Returns the number of the signal that caused the child process to stop its execution. - * - * It is only meaningful if hasBeenStopped() returns true. - * - * @return int - * - * @throws LogicException In case the process is not terminated - * - * @api - */ - public function getStopSignal() - { - $this->requireProcessIsTerminated(__FUNCTION__); - - $this->updateStatus(false); - - return $this->processInformation['stopsig']; - } - - /** - * Checks if the process is currently running. - * - * @return bool true if the process is currently running, false otherwise - */ - public function isRunning() - { - if (self::STATUS_STARTED !== $this->status) { - return false; - } - - $this->updateStatus(false); - - return $this->processInformation['running']; - } - - /** - * Checks if the process has been started with no regard to the current state. - * - * @return bool true if status is ready, false otherwise - */ - public function isStarted() - { - return $this->status != self::STATUS_READY; - } - - /** - * Checks if the process is terminated. - * - * @return bool true if process is terminated, false otherwise - */ - public function isTerminated() - { - $this->updateStatus(false); - - return $this->status == self::STATUS_TERMINATED; - } - - /** - * Gets the process status. - * - * The status is one of: ready, started, terminated. - * - * @return string The current process status - */ - public function getStatus() - { - $this->updateStatus(false); - - return $this->status; - } - - /** - * Stops the process. - * - * @param int|float $timeout The timeout in seconds - * @param int $signal A POSIX signal to send in case the process has not stop at timeout, default is SIGKILL - * - * @return int The exit-code of the process - * - * @throws RuntimeException if the process got signaled - */ - public function stop($timeout = 10, $signal = null) - { - $timeoutMicro = microtime(true) + $timeout; - if ($this->isRunning()) { - if (defined('PHP_WINDOWS_VERSION_BUILD') && !$this->isSigchildEnabled()) { - exec(sprintf("taskkill /F /T /PID %d 2>&1", $this->getPid()), $output, $exitCode); - if ($exitCode > 0) { - throw new RuntimeException('Unable to kill the process'); - } - } - proc_terminate($this->process); - do { - usleep(1000); - } while ($this->isRunning() && microtime(true) < $timeoutMicro); - - if ($this->isRunning() && !$this->isSigchildEnabled()) { - if (null !== $signal || defined('SIGKILL')) { - // avoid exception here : - // process is supposed to be running, but it might have stop - // just after this line. - // in any case, let's silently discard the error, we can not do anything - $this->doSignal($signal ?: SIGKILL, false); - } - } - } - - $this->updateStatus(false); - if ($this->processInformation['running']) { - $this->close(); - } - - return $this->exitcode; - } - - /** - * Adds a line to the STDOUT stream. - * - * @param string $line The line to append - */ - public function addOutput($line) - { - $this->lastOutputTime = microtime(true); - $this->stdout .= $line; - } - - /** - * Adds a line to the STDERR stream. - * - * @param string $line The line to append - */ - public function addErrorOutput($line) - { - $this->lastOutputTime = microtime(true); - $this->stderr .= $line; - } - - /** - * Gets the command line to be executed. - * - * @return string The command to execute - */ - public function getCommandLine() - { - return $this->commandline; - } - - /** - * Sets the command line to be executed. - * - * @param string $commandline The command to execute - * - * @return self The current Process instance - */ - public function setCommandLine($commandline) - { - $this->commandline = $commandline; - - return $this; - } - - /** - * Gets the process timeout (max. runtime). - * - * @return float|null The timeout in seconds or null if it's disabled - */ - public function getTimeout() - { - return $this->timeout; - } - - /** - * Gets the process idle timeout (max. time since last output). - * - * @return float|null The timeout in seconds or null if it's disabled - */ - public function getIdleTimeout() - { - return $this->idleTimeout; - } - - /** - * Sets the process timeout (max. runtime). - * - * To disable the timeout, set this value to null. - * - * @param int|float|null $timeout The timeout in seconds - * - * @return self The current Process instance - * - * @throws InvalidArgumentException if the timeout is negative - */ - public function setTimeout($timeout) - { - $this->timeout = $this->validateTimeout($timeout); - - return $this; - } - - /** - * Sets the process idle timeout (max. time since last output). - * - * To disable the timeout, set this value to null. - * - * @param int|float|null $timeout The timeout in seconds - * - * @return self The current Process instance. - * - * @throws InvalidArgumentException if the timeout is negative - */ - public function setIdleTimeout($timeout) - { - $this->idleTimeout = $this->validateTimeout($timeout); - - return $this; - } - - /** - * Enables or disables the TTY mode. - * - * @param bool $tty True to enabled and false to disable - * - * @return self The current Process instance - * - * @throws RuntimeException In case the TTY mode is not supported - */ - public function setTty($tty) - { - if (defined('PHP_WINDOWS_VERSION_BUILD') && $tty) { - throw new RuntimeException('TTY mode is not supported on Windows platform.'); - } - - $this->tty = (bool) $tty; - - return $this; - } - - /** - * Checks if the TTY mode is enabled. - * - * @return bool true if the TTY mode is enabled, false otherwise - */ - public function isTty() - { - return $this->tty; - } - - /** - * Gets the working directory. - * - * @return string|null The current working directory or null on failure - */ - public function getWorkingDirectory() - { - if (null === $this->cwd) { - // getcwd() will return false if any one of the parent directories does not have - // the readable or search mode set, even if the current directory does - return getcwd() ?: null; - } - - return $this->cwd; - } - - /** - * Sets the current working directory. - * - * @param string $cwd The new working directory - * - * @return self The current Process instance - */ - public function setWorkingDirectory($cwd) - { - $this->cwd = $cwd; - - return $this; - } - - /** - * Gets the environment variables. - * - * @return array The current environment variables - */ - public function getEnv() - { - return $this->env; - } - - /** - * Sets the environment variables. - * - * An environment variable value should be a string. - * If it is an array, the variable is ignored. - * - * That happens in PHP when 'argv' is registered into - * the $_ENV array for instance. - * - * @param array $env The new environment variables - * - * @return self The current Process instance - */ - public function setEnv(array $env) - { - // Process can not handle env values that are arrays - $env = array_filter($env, function ($value) { - return !is_array($value); - }); - - $this->env = array(); - foreach ($env as $key => $value) { - $this->env[(binary) $key] = (binary) $value; - } - - return $this; - } - - /** - * Gets the contents of STDIN. - * - * @return string|null The current contents - */ - public function getStdin() - { - return $this->stdin; - } - - /** - * Sets the contents of STDIN. - * - * @param string|null $stdin The new contents - * - * @return self The current Process instance - * - * @throws LogicException In case the process is running - */ - public function setStdin($stdin) - { - if ($this->isRunning()) { - throw new LogicException('STDIN can not be set while the process is running.'); - } - - $this->stdin = $stdin; - - return $this; - } - - /** - * Gets the options for proc_open. - * - * @return array The current options - */ - public function getOptions() - { - return $this->options; - } - - /** - * Sets the options for proc_open. - * - * @param array $options The new options - * - * @return self The current Process instance - */ - public function setOptions(array $options) - { - $this->options = $options; - - return $this; - } - - /** - * Gets whether or not Windows compatibility is enabled. - * - * This is true by default. - * - * @return bool - */ - public function getEnhanceWindowsCompatibility() - { - return $this->enhanceWindowsCompatibility; - } - - /** - * Sets whether or not Windows compatibility is enabled. - * - * @param bool $enhance - * - * @return self The current Process instance - */ - public function setEnhanceWindowsCompatibility($enhance) - { - $this->enhanceWindowsCompatibility = (bool) $enhance; - - return $this; - } - - /** - * Returns whether sigchild compatibility mode is activated or not. - * - * @return bool - */ - public function getEnhanceSigchildCompatibility() - { - return $this->enhanceSigchildCompatibility; - } - - /** - * Activates sigchild compatibility mode. - * - * Sigchild compatibility mode is required to get the exit code and - * determine the success of a process when PHP has been compiled with - * the --enable-sigchild option - * - * @param bool $enhance - * - * @return self The current Process instance - */ - public function setEnhanceSigchildCompatibility($enhance) - { - $this->enhanceSigchildCompatibility = (bool) $enhance; - - return $this; - } - - /** - * Performs a check between the timeout definition and the time the process started. - * - * In case you run a background process (with the start method), you should - * trigger this method regularly to ensure the process timeout - * - * @throws ProcessTimedOutException In case the timeout was reached - */ - public function checkTimeout() - { - if ($this->status !== self::STATUS_STARTED) { - return; - } - - if (null !== $this->timeout && $this->timeout < microtime(true) - $this->starttime) { - $this->stop(0); - - throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_GENERAL); - } - - if (null !== $this->idleTimeout && $this->idleTimeout < microtime(true) - $this->lastOutputTime) { - $this->stop(0); - - throw new ProcessTimedOutException($this, ProcessTimedOutException::TYPE_IDLE); - } - } - - /** - * Creates the descriptors needed by the proc_open. - * - * @return array - */ - private function getDescriptors() - { - $this->processPipes = new ProcessPipes($this->useFileHandles, $this->tty); - $descriptors = $this->processPipes->getDescriptors(); - - if (!$this->useFileHandles && $this->enhanceSigchildCompatibility && $this->isSigchildEnabled()) { - // last exit code is output on the fourth pipe and caught to work around --enable-sigchild - $descriptors = array_merge($descriptors, array(array('pipe', 'w'))); - - $this->commandline = '('.$this->commandline.') 3>/dev/null; code=$?; echo $code >&3; exit $code'; - } - - return $descriptors; - } - - /** - * Builds up the callback used by wait(). - * - * The callbacks adds all occurred output to the specific buffer and calls - * the user callback (if present) with the received output. - * - * @param callable|null $callback The user defined PHP callback - * - * @return callable A PHP callable - */ - protected function buildCallback($callback) - { - $that = $this; - $out = self::OUT; - $err = self::ERR; - $callback = function ($type, $data) use ($that, $callback, $out, $err) { - if ($out == $type) { - $that->addOutput($data); - } else { - $that->addErrorOutput($data); - } - - if (null !== $callback) { - call_user_func($callback, $type, $data); - } - }; - - return $callback; - } - - /** - * Updates the status of the process, reads pipes. - * - * @param bool $blocking Whether to use a blocking read call. - */ - protected function updateStatus($blocking) - { - if (self::STATUS_STARTED !== $this->status) { - return; - } - - $this->processInformation = proc_get_status($this->process); - $this->captureExitCode(); - - $this->readPipes($blocking, defined('PHP_WINDOWS_VERSION_BUILD') ? !$this->processInformation['running'] : true); - - if (!$this->processInformation['running']) { - $this->close(); - } - } - - /** - * Returns whether PHP has been compiled with the '--enable-sigchild' option or not. - * - * @return bool - */ - protected function isSigchildEnabled() - { - if (null !== self::$sigchild) { - return self::$sigchild; - } - - if (!function_exists('phpinfo')) { - return self::$sigchild = false; - } - - ob_start(); - phpinfo(INFO_GENERAL); - - return self::$sigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); - } - - /** - * Validates and returns the filtered timeout. - * - * @param int|float|null $timeout - * - * @return float|null - */ - private function validateTimeout($timeout) - { - $timeout = (float) $timeout; - - if (0.0 === $timeout) { - $timeout = null; - } elseif ($timeout < 0) { - throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); - } - - return $timeout; - } - - /** - * Reads pipes, executes callback. - * - * @param bool $blocking Whether to use blocking calls or not. - * @param bool $close Whether to close file handles or not. - */ - private function readPipes($blocking, $close) - { - if ($close) { - $result = $this->processPipes->readAndCloseHandles($blocking); - } else { - $result = $this->processPipes->read($blocking); - } - - foreach ($result as $type => $data) { - if (3 == $type) { - $this->fallbackExitcode = (int) $data; - } else { - call_user_func($this->callback, $type === self::STDOUT ? self::OUT : self::ERR, $data); - } - } - } - - /** - * Captures the exitcode if mentioned in the process information. - */ - private function captureExitCode() - { - if (isset($this->processInformation['exitcode']) && -1 != $this->processInformation['exitcode']) { - $this->exitcode = $this->processInformation['exitcode']; - } - } - - /** - * Closes process resource, closes file handles, sets the exitcode. - * - * @return int The exitcode - */ - private function close() - { - $this->processPipes->close(); - if (is_resource($this->process)) { - $exitcode = proc_close($this->process); - } else { - $exitcode = -1; - } - - $this->exitcode = -1 !== $exitcode ? $exitcode : (null !== $this->exitcode ? $this->exitcode : -1); - $this->status = self::STATUS_TERMINATED; - - if (-1 === $this->exitcode && null !== $this->fallbackExitcode) { - $this->exitcode = $this->fallbackExitcode; - } elseif (-1 === $this->exitcode && $this->processInformation['signaled'] && 0 < $this->processInformation['termsig']) { - // if process has been signaled, no exitcode but a valid termsig, apply Unix convention - $this->exitcode = 128 + $this->processInformation['termsig']; - } - - return $this->exitcode; - } - - /** - * Resets data related to the latest run of the process. - */ - private function resetProcessData() - { - $this->starttime = null; - $this->callback = null; - $this->exitcode = null; - $this->fallbackExitcode = null; - $this->processInformation = null; - $this->stdout = null; - $this->stderr = null; - $this->process = null; - $this->status = self::STATUS_READY; - $this->incrementalOutputOffset = 0; - $this->incrementalErrorOutputOffset = 0; - } - - /** - * Sends a POSIX signal to the process. - * - * @param int $signal A valid POSIX signal (see http://www.php.net/manual/en/pcntl.constants.php) - * @param bool $throwException Whether to throw exception in case signal failed - * - * @return bool True if the signal was sent successfully, false otherwise - * - * @throws LogicException In case the process is not running - * @throws RuntimeException In case --enable-sigchild is activated - * @throws RuntimeException In case of failure - */ - private function doSignal($signal, $throwException) - { - if (!$this->isRunning()) { - if ($throwException) { - throw new LogicException('Can not send signal on a non running process.'); - } - - return false; - } - - if ($this->isSigchildEnabled()) { - if ($throwException) { - throw new RuntimeException('This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - } - - return false; - } - - if (true !== @proc_terminate($this->process, $signal)) { - if ($throwException) { - throw new RuntimeException(sprintf('Error while sending signal `%s`.', $signal)); - } - - return false; - } - - return true; - } - - /** - * Ensures the process is running or terminated, throws a LogicException if the process has a not started. - * - * @param string $functionName The function name that was called. - * - * @throws LogicException If the process has not run. - */ - private function requireProcessIsStarted($functionName) - { - if (!$this->isStarted()) { - throw new LogicException(sprintf('Process must be started before calling %s.', $functionName)); - } - } - - /** - * Ensures the process is terminated, throws a LogicException if the process has a status different than `terminated`. - * - * @param string $functionName The function name that was called. - * - * @throws LogicException If the process is not yet terminated. - */ - private function requireProcessIsTerminated($functionName) - { - if (!$this->isTerminated()) { - throw new LogicException(sprintf('Process must be terminated before calling %s.', $functionName)); - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php deleted file mode 100644 index c9a77d448..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/ProcessBuilder.php +++ /dev/null @@ -1,241 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -use Symfony\Component\Process\Exception\InvalidArgumentException; -use Symfony\Component\Process\Exception\LogicException; - -/** - * Process builder. - * - * @author Kris Wallsmith - */ -class ProcessBuilder -{ - private $arguments; - private $cwd; - private $env = array(); - private $stdin; - private $timeout = 60; - private $options = array(); - private $inheritEnv = true; - private $prefix = array(); - - /** - * Constructor - * - * @param string[] $arguments An array of arguments - */ - public function __construct(array $arguments = array()) - { - $this->arguments = $arguments; - } - - /** - * Creates a process builder instance. - * - * @param string[] $arguments An array of arguments - * - * @return ProcessBuilder - */ - public static function create(array $arguments = array()) - { - return new static($arguments); - } - - /** - * Adds an unescaped argument to the command string. - * - * @param string $argument A command argument - * - * @return ProcessBuilder - */ - public function add($argument) - { - $this->arguments[] = $argument; - - return $this; - } - - /** - * Adds an unescaped prefix to the command string. - * - * The prefix is preserved when resetting arguments. - * - * @param string|array $prefix A command prefix or an array of command prefixes - * - * @return ProcessBuilder - */ - public function setPrefix($prefix) - { - $this->prefix = is_array($prefix) ? $prefix : array($prefix); - - return $this; - } - - /** - * Sets the arguments of the process. - * - * Arguments must not be escaped. - * Previous arguments are removed. - * - * @param string[] $arguments - * - * @return ProcessBuilder - */ - public function setArguments(array $arguments) - { - $this->arguments = $arguments; - - return $this; - } - - /** - * Sets the working directory. - * - * @param null|string $cwd The working directory - * - * @return ProcessBuilder - */ - public function setWorkingDirectory($cwd) - { - $this->cwd = $cwd; - - return $this; - } - - /** - * Sets whether environment variables will be inherited or not. - * - * @param bool $inheritEnv - * - * @return ProcessBuilder - */ - public function inheritEnvironmentVariables($inheritEnv = true) - { - $this->inheritEnv = $inheritEnv; - - return $this; - } - - /** - * Sets an environment variable - * - * Setting a variable overrides its previous value. Use `null` to unset a - * defined environment variable. - * - * @param string $name The variable name - * @param null|string $value The variable value - * - * @return ProcessBuilder - */ - public function setEnv($name, $value) - { - $this->env[$name] = $value; - - return $this; - } - - public function addEnvironmentVariables(array $variables) - { - $this->env = array_replace($this->env, $variables); - - return $this; - } - - /** - * Sets the input of the process. - * - * @param string $stdin The input as a string - * - * @return ProcessBuilder - */ - public function setInput($stdin) - { - $this->stdin = $stdin; - - return $this; - } - - /** - * Sets the process timeout. - * - * To disable the timeout, set this value to null. - * - * @param float|null - * - * @return ProcessBuilder - * - * @throws InvalidArgumentException - */ - public function setTimeout($timeout) - { - if (null === $timeout) { - $this->timeout = null; - - return $this; - } - - $timeout = (float) $timeout; - - if ($timeout < 0) { - throw new InvalidArgumentException('The timeout value must be a valid positive integer or float number.'); - } - - $this->timeout = $timeout; - - return $this; - } - - /** - * Adds a proc_open option. - * - * @param string $name The option name - * @param string $value The option value - * - * @return ProcessBuilder - */ - public function setOption($name, $value) - { - $this->options[$name] = $value; - - return $this; - } - - /** - * Creates a Process instance and returns it. - * - * @return Process - * - * @throws LogicException In case no arguments have been provided - */ - public function getProcess() - { - if (0 === count($this->prefix) && 0 === count($this->arguments)) { - throw new LogicException('You must add() command arguments before calling getProcess().'); - } - - $options = $this->options; - - $arguments = array_merge($this->prefix, $this->arguments); - $script = implode(' ', array_map(array(__NAMESPACE__.'\\ProcessUtils', 'escapeArgument'), $arguments)); - - if ($this->inheritEnv) { - // include $_ENV for BC purposes - $env = array_replace($_ENV, $_SERVER, $this->env); - } else { - $env = $this->env; - } - - return new Process($script, $this->cwd, $env, $this->stdin, $this->timeout, $options); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php deleted file mode 100644 index f35d1c128..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/ProcessPipes.php +++ /dev/null @@ -1,358 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -use Symfony\Component\Process\Exception\RuntimeException; - -/** - * ProcessPipes manages descriptors and pipes for the use of proc_open. - */ -class ProcessPipes -{ - /** @var array */ - public $pipes = array(); - /** @var array */ - private $files = array(); - /** @var array */ - private $fileHandles = array(); - /** @var array */ - private $readBytes = array(); - /** @var bool */ - private $useFiles; - /** @var bool */ - private $ttyMode; - - const CHUNK_SIZE = 16384; - - public function __construct($useFiles, $ttyMode) - { - $this->useFiles = (bool) $useFiles; - $this->ttyMode = (bool) $ttyMode; - - // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. - // Workaround for this problem is to use temporary files instead of pipes on Windows platform. - // - // @see https://bugs.php.net/bug.php?id=51800 - if ($this->useFiles) { - $this->files = array( - Process::STDOUT => tempnam(sys_get_temp_dir(), 'sf_proc_stdout'), - Process::STDERR => tempnam(sys_get_temp_dir(), 'sf_proc_stderr'), - ); - foreach ($this->files as $offset => $file) { - $this->fileHandles[$offset] = fopen($this->files[$offset], 'rb'); - if (false === $this->fileHandles[$offset]) { - throw new RuntimeException('A temporary file could not be opened to write the process output to, verify that your TEMP environment variable is writable'); - } - } - $this->readBytes = array( - Process::STDOUT => 0, - Process::STDERR => 0, - ); - } - } - - public function __destruct() - { - $this->close(); - $this->removeFiles(); - } - - /** - * Sets non-blocking mode on pipes. - */ - public function unblock() - { - foreach ($this->pipes as $pipe) { - stream_set_blocking($pipe, 0); - } - } - - /** - * Closes file handles and pipes. - */ - public function close() - { - $this->closeUnixPipes(); - foreach ($this->fileHandles as $handle) { - fclose($handle); - } - $this->fileHandles = array(); - } - - /** - * Closes Unix pipes. - * - * Nothing happens in case file handles are used. - */ - public function closeUnixPipes() - { - foreach ($this->pipes as $pipe) { - fclose($pipe); - } - $this->pipes = array(); - } - - /** - * Returns an array of descriptors for the use of proc_open. - * - * @return array - */ - public function getDescriptors() - { - if ($this->useFiles) { - // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800) - // We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650 - // So we redirect output within the commandline and pass the nul device to the process - return array( - array('pipe', 'r'), - array('file', 'NUL', 'w'), - array('file', 'NUL', 'w'), - ); - } - - if ($this->ttyMode) { - return array( - array('file', '/dev/tty', 'r'), - array('file', '/dev/tty', 'w'), - array('file', '/dev/tty', 'w'), - ); - } - - return array( - array('pipe', 'r'), // stdin - array('pipe', 'w'), // stdout - array('pipe', 'w'), // stderr - ); - } - - /** - * Returns an array of filenames indexed by their related stream in case these pipes use temporary files. - * - * @return array - */ - public function getFiles() - { - if ($this->useFiles) { - return $this->files; - } - - return array(); - } - - /** - * Reads data in file handles and pipes. - * - * @param bool $blocking Whether to use blocking calls or not. - * - * @return array An array of read data indexed by their fd. - */ - public function read($blocking) - { - return array_replace($this->readStreams($blocking), $this->readFileHandles()); - } - - /** - * Reads data in file handles and pipes, closes them if EOF is reached. - * - * @param bool $blocking Whether to use blocking calls or not. - * - * @return array An array of read data indexed by their fd. - */ - public function readAndCloseHandles($blocking) - { - return array_replace($this->readStreams($blocking, true), $this->readFileHandles(true)); - } - - /** - * Returns if the current state has open file handles or pipes. - * - * @return bool - */ - public function hasOpenHandles() - { - if (!$this->useFiles) { - return (bool) $this->pipes; - } - - return (bool) $this->pipes && (bool) $this->fileHandles; - } - - /** - * Writes stdin data. - * - * @param bool $blocking Whether to use blocking calls or not. - * @param string|null $stdin The data to write. - */ - public function write($blocking, $stdin) - { - if (null === $stdin) { - fclose($this->pipes[0]); - unset($this->pipes[0]); - - return; - } - - $writePipes = array($this->pipes[0]); - unset($this->pipes[0]); - $stdinLen = strlen($stdin); - $stdinOffset = 0; - - while ($writePipes) { - $r = null; - $w = $writePipes; - $e = null; - - if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { - // if a system call has been interrupted, forget about it, let's try again - if ($this->hasSystemCallBeenInterrupted()) { - continue; - } - break; - } - - // nothing has changed, let's wait until the process is ready - if (0 === $n) { - continue; - } - - if ($w) { - $written = fwrite($writePipes[0], (binary) substr($stdin, $stdinOffset), 8192); - if (false !== $written) { - $stdinOffset += $written; - } - if ($stdinOffset >= $stdinLen) { - fclose($writePipes[0]); - $writePipes = null; - } - } - } - } - - /** - * Reads data in file handles. - * - * @param bool $close Whether to close file handles or not. - * - * @return array An array of read data indexed by their fd. - */ - private function readFileHandles($close = false) - { - $read = array(); - $fh = $this->fileHandles; - foreach ($fh as $type => $fileHandle) { - if (0 !== fseek($fileHandle, $this->readBytes[$type])) { - continue; - } - $data = ''; - $dataread = null; - while (!feof($fileHandle)) { - if (false !== $dataread = fread($fileHandle, self::CHUNK_SIZE)) { - $data .= $dataread; - } - } - if (0 < $length = strlen($data)) { - $this->readBytes[$type] += $length; - $read[$type] = $data; - } - - if (false === $dataread || (true === $close && feof($fileHandle) && '' === $data)) { - fclose($this->fileHandles[$type]); - unset($this->fileHandles[$type]); - } - } - - return $read; - } - - /** - * Reads data in file pipes streams. - * - * @param bool $blocking Whether to use blocking calls or not. - * @param bool $close Whether to close file handles or not. - * - * @return array An array of read data indexed by their fd. - */ - private function readStreams($blocking, $close = false) - { - if (empty($this->pipes)) { - return array(); - } - - $read = array(); - - $r = $this->pipes; - $w = null; - $e = null; - - // let's have a look if something changed in streams - if (false === $n = @stream_select($r, $w, $e, 0, $blocking ? ceil(Process::TIMEOUT_PRECISION * 1E6) : 0)) { - // if a system call has been interrupted, forget about it, let's try again - // otherwise, an error occurred, let's reset pipes - if (!$this->hasSystemCallBeenInterrupted()) { - $this->pipes = array(); - } - - return $read; - } - - // nothing has changed - if (0 === $n) { - return $read; - } - - foreach ($r as $pipe) { - $type = array_search($pipe, $this->pipes); - - $data = ''; - while ($dataread = fread($pipe, self::CHUNK_SIZE)) { - $data .= $dataread; - } - - if ($data) { - $read[$type] = $data; - } - - if (false === $data || (true === $close && feof($pipe) && '' === $data)) { - fclose($this->pipes[$type]); - unset($this->pipes[$type]); - } - } - - return $read; - } - - /** - * Returns true if a system call has been interrupted. - * - * @return bool - */ - private function hasSystemCallBeenInterrupted() - { - $lastError = error_get_last(); - - // stream_select returns false when the `select` system call is interrupted by an incoming signal - return isset($lastError['message']) && false !== stripos($lastError['message'], 'interrupted system call'); - } - - /** - * Removes temporary files - */ - private function removeFiles() - { - foreach ($this->files as $filename) { - if (file_exists($filename)) { - @unlink($filename); - } - } - $this->files = array(); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php b/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php deleted file mode 100644 index 5317cd088..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/ProcessUtils.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process; - -/** - * ProcessUtils is a bunch of utility methods. - * - * This class contains static methods only and is not meant to be instantiated. - * - * @author Martin Hasoň - */ -class ProcessUtils -{ - /** - * This class should not be instantiated - */ - private function __construct() - { - } - - /** - * Escapes a string to be used as a shell argument. - * - * @param string $argument The argument that will be escaped - * - * @return string The escaped argument - */ - public static function escapeArgument($argument) - { - //Fix for PHP bug #43784 escapeshellarg removes % from given string - //Fix for PHP bug #49446 escapeshellarg doesn't work on Windows - //@see https://bugs.php.net/bug.php?id=43784 - //@see https://bugs.php.net/bug.php?id=49446 - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - if ('' === $argument) { - return escapeshellarg($argument); - } - - $escapedArgument = ''; - $quote = false; - foreach (preg_split('/(")/i', $argument, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE) as $part) { - if ('"' === $part) { - $escapedArgument .= '\\"'; - } elseif (self::isSurroundedBy($part, '%')) { - // Avoid environment variable expansion - $escapedArgument .= '^%"'.substr($part, 1, -1).'"^%'; - } else { - // escape trailing backslash - if ('\\' === substr($part, -1)) { - $part .= '\\'; - } - $quote = true; - $escapedArgument .= $part; - } - } - if ($quote) { - $escapedArgument = '"'.$escapedArgument.'"'; - } - - return $escapedArgument; - } - - return escapeshellarg($argument); - } - - private static function isSurroundedBy($arg, $char) - { - return 2 < strlen($arg) && $char === $arg[0] && $char === $arg[strlen($arg) - 1]; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/README.md b/core/lib/symfony/process/Symfony/Component/Process/README.md deleted file mode 100644 index 9bcc656b7..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/README.md +++ /dev/null @@ -1,47 +0,0 @@ -Process Component -================= - -Process executes commands in sub-processes. - -In this example, we run a simple directory listing and get the result back: - - use Symfony\Component\Process\Process; - - $process = new Process('ls -lsa'); - $process->setTimeout(3600); - $process->run(); - if (!$process->isSuccessful()) { - throw new RuntimeException($process->getErrorOutput()); - } - - print $process->getOutput(); - -You can think that this is easy to achieve with plain PHP but it's not especially -if you want to take care of the subtle differences between the different platforms. - -And if you want to be able to get some feedback in real-time, just pass an -anonymous function to the ``run()`` method and you will get the output buffer -as it becomes available: - - use Symfony\Component\Process\Process; - - $process = new Process('ls -lsa'); - $process->run(function ($type, $buffer) { - if ('err' === $type) { - echo 'ERR > '.$buffer; - } else { - echo 'OUT > '.$buffer; - } - }); - -That's great if you want to execute a long running command (like rsync-ing files to a -remote server) and give feedback to the user in real-time. - -Resources ---------- - -You can run the unit tests with the following command: - - $ cd path/to/Symfony/Component/XXX/ - $ composer.phar install - $ phpunit diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php deleted file mode 100644 index 9522bd402..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/AbstractProcessTest.php +++ /dev/null @@ -1,873 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\Exception\ProcessTimedOutException; -use Symfony\Component\Process\Exception\LogicException; -use Symfony\Component\Process\Process; -use Symfony\Component\Process\Exception\RuntimeException; -use Symfony\Component\Process\ProcessPipes; - -/** - * @author Robert Schönthal - */ -abstract class AbstractProcessTest extends \PHPUnit_Framework_TestCase -{ - public function testThatProcessDoesNotThrowWarningDuringRun() - { - @trigger_error('Test Error', E_USER_NOTICE); - $process = $this->getProcess("php -r 'sleep(3)'"); - $process->run(); - $actualError = error_get_last(); - $this->assertEquals('Test Error', $actualError['message']); - $this->assertEquals(E_USER_NOTICE, $actualError['type']); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException - */ - public function testNegativeTimeoutFromConstructor() - { - $this->getProcess('', null, null, null, -1); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException - */ - public function testNegativeTimeoutFromSetter() - { - $p = $this->getProcess(''); - $p->setTimeout(-1); - } - - public function testFloatAndNullTimeout() - { - $p = $this->getProcess(''); - - $p->setTimeout(10); - $this->assertSame(10.0, $p->getTimeout()); - - $p->setTimeout(null); - $this->assertNull($p->getTimeout()); - - $p->setTimeout(0.0); - $this->assertNull($p->getTimeout()); - } - - public function testStopWithTimeoutIsActuallyWorking() - { - $this->verifyPosixIsEnabled(); - - // exec is mandatory here since we send a signal to the process - // see https://github.com/symfony/symfony/issues/5030 about prepending - // command with exec - $p = $this->getProcess('exec php '.__DIR__.'/NonStopableProcess.php 3'); - $p->start(); - usleep(100000); - $start = microtime(true); - $p->stop(1.1, SIGKILL); - while ($p->isRunning()) { - usleep(1000); - } - $duration = microtime(true) - $start; - - $this->assertLessThan(1.8, $duration); - } - - public function testAllOutputIsActuallyReadOnTermination() - { - // this code will result in a maximum of 2 reads of 8192 bytes by calling - // start() and isRunning(). by the time getOutput() is called the process - // has terminated so the internal pipes array is already empty. normally - // the call to start() will not read any data as the process will not have - // generated output, but this is non-deterministic so we must count it as - // a possibility. therefore we need 2 * ProcessPipes::CHUNK_SIZE plus - // another byte which will never be read. - $expectedOutputSize = ProcessPipes::CHUNK_SIZE * 2 + 2; - - $code = sprintf('echo str_repeat(\'*\', %d);', $expectedOutputSize); - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); - - $p->start(); - // Let's wait enough time for process to finish... - // Here we don't call Process::run or Process::wait to avoid any read of pipes - usleep(500000); - - if ($p->isRunning()) { - $this->markTestSkipped('Process execution did not complete in the required time frame'); - } - - $o = $p->getOutput(); - - $this->assertEquals($expectedOutputSize, strlen($o)); - } - - public function testCallbacksAreExecutedWithStart() - { - $data = ''; - - $process = $this->getProcess('echo foo && php -r "sleep(1);" && echo foo'); - $process->start(function ($type, $buffer) use (&$data) { - $data .= $buffer; - }); - - while ($process->isRunning()) { - usleep(10000); - } - - $this->assertEquals(2, preg_match_all('/foo/', $data, $matches)); - } - - /** - * tests results from sub processes - * - * @dataProvider responsesCodeProvider - */ - public function testProcessResponses($expected, $getter, $code) - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); - $p->run(); - - $this->assertSame($expected, $p->$getter()); - } - - /** - * tests results from sub processes - * - * @dataProvider pipesCodeProvider - */ - public function testProcessPipes($code, $size) - { - $expected = str_repeat(str_repeat('*', 1024), $size) . '!'; - $expectedLength = (1024 * $size) + 1; - - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg($code))); - $p->setStdin($expected); - $p->run(); - - $this->assertEquals($expectedLength, strlen($p->getOutput())); - $this->assertEquals($expectedLength, strlen($p->getErrorOutput())); - } - - public function testSetStdinWhileRunningThrowsAnException() - { - $process = $this->getProcess('php -r "usleep(500000);"'); - $process->start(); - try { - $process->setStdin('foobar'); - $process->stop(); - $this->fail('A LogicException should have been raised.'); - } catch (LogicException $e) { - $this->assertEquals('STDIN can not be set while the process is running.', $e->getMessage()); - } - $process->stop(); - } - - public function chainedCommandsOutputProvider() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - return array( - array("2 \r\n2\r\n", '&&', '2') - ); - } - - return array( - array("1\n1\n", ';', '1'), - array("2\n2\n", '&&', '2'), - ); - } - - /** - * - * @dataProvider chainedCommandsOutputProvider - */ - public function testChainedCommandsOutput($expected, $operator, $input) - { - $process = $this->getProcess(sprintf('echo %s %s echo %s', $input, $operator, $input)); - $process->run(); - $this->assertEquals($expected, $process->getOutput()); - } - - public function testCallbackIsExecutedForOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('echo \'foo\';'))); - - $called = false; - $p->run(function ($type, $buffer) use (&$called) { - $called = $buffer === 'foo'; - }); - - $this->assertTrue($called, 'The callback should be executed with the output'); - } - - public function testGetErrorOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); - - $p->run(); - $this->assertEquals(3, preg_match_all('/ERROR/', $p->getErrorOutput(), $matches)); - } - - public function testGetIncrementalErrorOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { usleep(100000); file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); - - $p->start(); - while ($p->isRunning()) { - $this->assertLessThanOrEqual(1, preg_match_all('/ERROR/', $p->getIncrementalErrorOutput(), $matches)); - usleep(20000); - } - } - - public function testFlushErrorOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n = 0; while ($n < 3) { file_put_contents(\'php://stderr\', \'ERROR\'); $n++; }'))); - - $p->run(); - $p->clearErrorOutput(); - $this->assertEmpty($p->getErrorOutput()); - } - - public function testGetOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++; usleep(500); }'))); - - $p->run(); - $this->assertEquals(3, preg_match_all('/foo/', $p->getOutput(), $matches)); - } - - public function testGetIncrementalOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) { echo \' foo \'; usleep(50000); $n++; }'))); - - $p->start(); - while ($p->isRunning()) { - $this->assertLessThanOrEqual(1, preg_match_all('/foo/', $p->getIncrementalOutput(), $matches)); - usleep(20000); - } - } - - public function testFlushOutput() - { - $p = $this->getProcess(sprintf('php -r %s', escapeshellarg('$n=0;while ($n<3) {echo \' foo \';$n++;}'))); - - $p->run(); - $p->clearOutput(); - $this->assertEmpty($p->getOutput()); - } - - public function testExitCodeCommandFailed() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX exit code'); - } - - // such command run in bash return an exitcode 127 - $process = $this->getProcess('nonexistingcommandIhopeneversomeonewouldnameacommandlikethis'); - $process->run(); - - $this->assertGreaterThan(0, $process->getExitCode()); - } - - public function testTTYCommand() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does have /dev/tty support'); - } - - $process = $this->getProcess('echo "foo" >> /dev/null && php -r "usleep(100000);"'); - $process->setTty(true); - $process->start(); - $this->assertTrue($process->isRunning()); - $process->wait(); - - $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); - } - - public function testTTYCommandExitCode() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does have /dev/tty support'); - } - - $process = $this->getProcess('echo "foo" >> /dev/null'); - $process->setTty(true); - $process->run(); - - $this->assertTrue($process->isSuccessful()); - } - - public function testTTYInWindowsEnvironment() - { - if (!defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('This test is for Windows platform only'); - } - - $process = $this->getProcess('echo "foo" >> /dev/null'); - $process->setTty(false); - $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'TTY mode is not supported on Windows platform.'); - $process->setTty(true); - } - - public function testExitCodeTextIsNullWhenExitCodeIsNull() - { - $process = $this->getProcess(''); - $this->assertNull($process->getExitCodeText()); - } - - public function testExitCodeText() - { - $process = $this->getProcess(''); - $r = new \ReflectionObject($process); - $p = $r->getProperty('exitcode'); - $p->setAccessible(true); - - $p->setValue($process, 2); - $this->assertEquals('Misuse of shell builtins', $process->getExitCodeText()); - } - - public function testStartIsNonBlocking() - { - $process = $this->getProcess('php -r "usleep(500000);"'); - $start = microtime(true); - $process->start(); - $end = microtime(true); - $this->assertLessThan(0.2, $end-$start); - $process->wait(); - } - - public function testUpdateStatus() - { - $process = $this->getProcess('php -h'); - $process->run(); - $this->assertTrue(strlen($process->getOutput()) > 0); - } - - public function testGetExitCodeIsNullOnStart() - { - $process = $this->getProcess('php -r "usleep(200000);"'); - $this->assertNull($process->getExitCode()); - $process->start(); - $this->assertNull($process->getExitCode()); - $process->wait(); - $this->assertEquals(0, $process->getExitCode()); - } - - public function testGetExitCodeIsNullOnWhenStartingAgain() - { - $process = $this->getProcess('php -r "usleep(200000);"'); - $process->run(); - $this->assertEquals(0, $process->getExitCode()); - $process->start(); - $this->assertNull($process->getExitCode()); - $process->wait(); - $this->assertEquals(0, $process->getExitCode()); - } - - public function testGetExitCode() - { - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertSame(0, $process->getExitCode()); - } - - public function testStatus() - { - $process = $this->getProcess('php -r "usleep(500000);"'); - $this->assertFalse($process->isRunning()); - $this->assertFalse($process->isStarted()); - $this->assertFalse($process->isTerminated()); - $this->assertSame(Process::STATUS_READY, $process->getStatus()); - $process->start(); - $this->assertTrue($process->isRunning()); - $this->assertTrue($process->isStarted()); - $this->assertFalse($process->isTerminated()); - $this->assertSame(Process::STATUS_STARTED, $process->getStatus()); - $process->wait(); - $this->assertFalse($process->isRunning()); - $this->assertTrue($process->isStarted()); - $this->assertTrue($process->isTerminated()); - $this->assertSame(Process::STATUS_TERMINATED, $process->getStatus()); - } - - public function testStop() - { - $process = $this->getProcess('php -r "sleep(4);"'); - $process->start(); - $this->assertTrue($process->isRunning()); - $process->stop(); - $this->assertFalse($process->isRunning()); - } - - public function testIsSuccessful() - { - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertTrue($process->isSuccessful()); - } - - public function testIsSuccessfulOnlyAfterTerminated() - { - $process = $this->getProcess('php -r "sleep(1);"'); - $process->start(); - while ($process->isRunning()) { - $this->assertFalse($process->isSuccessful()); - usleep(300000); - } - - $this->assertTrue($process->isSuccessful()); - } - - public function testIsNotSuccessful() - { - $process = $this->getProcess('php -r "usleep(500000);throw new \Exception(\'BOUM\');"'); - $process->start(); - $this->assertTrue($process->isRunning()); - $process->wait(); - $this->assertFalse($process->isSuccessful()); - } - - public function testProcessIsNotSignaled() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertFalse($process->hasBeenSignaled()); - } - - public function testProcessWithoutTermSignalIsNotSignaled() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertFalse($process->hasBeenSignaled()); - } - - public function testProcessWithoutTermSignal() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertEquals(0, $process->getTermSignal()); - } - - public function testProcessIsSignaledIfStopped() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - $process = $this->getProcess('php -r "sleep(4);"'); - $process->start(); - $process->stop(); - $this->assertTrue($process->hasBeenSignaled()); - } - - public function testProcessWithTermSignal() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - // SIGTERM is only defined if pcntl extension is present - $termSignal = defined('SIGTERM') ? SIGTERM : 15; - - $process = $this->getProcess('php -r "sleep(4);"'); - $process->start(); - $process->stop(); - - $this->assertEquals($termSignal, $process->getTermSignal()); - } - - public function testProcessThrowsExceptionWhenExternallySignaled() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Windows does not support POSIX signals'); - } - - if (!function_exists('posix_kill')) { - $this->markTestSkipped('posix_kill is required for this test'); - } - - $termSignal = defined('SIGKILL') ? SIGKILL : 9; - - $process = $this->getProcess('exec php -r "while (true) {}"'); - $process->start(); - posix_kill($process->getPid(), $termSignal); - - $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'The process has been signaled with signal "9".'); - $process->wait(); - } - - public function testRestart() - { - $process1 = $this->getProcess('php -r "echo getmypid();"'); - $process1->run(); - $process2 = $process1->restart(); - - $process2->wait(); // wait for output - - // Ensure that both processed finished and the output is numeric - $this->assertFalse($process1->isRunning()); - $this->assertFalse($process2->isRunning()); - $this->assertTrue(is_numeric($process1->getOutput())); - $this->assertTrue(is_numeric($process2->getOutput())); - - // Ensure that restart returned a new process by check that the output is different - $this->assertNotEquals($process1->getOutput(), $process2->getOutput()); - } - - public function testPhpDeadlock() - { - $this->markTestSkipped('Can course PHP to hang'); - - // Sleep doesn't work as it will allow the process to handle signals and close - // file handles from the other end. - $process = $this->getProcess('php -r "while (true) {}"'); - $process->start(); - - // PHP will deadlock when it tries to cleanup $process - } - - public function testRunProcessWithTimeout() - { - $timeout = 0.5; - $process = $this->getProcess('php -r "usleep(600000);"'); - $process->setTimeout($timeout); - $start = microtime(true); - try { - $process->run(); - $this->fail('A RuntimeException should have been raised'); - } catch (RuntimeException $e) { - - } - $duration = microtime(true) - $start; - - $this->assertLessThan($timeout + Process::TIMEOUT_PRECISION, $duration); - } - - public function testCheckTimeoutOnNonStartedProcess() - { - $process = $this->getProcess('php -r "sleep(3);"'); - $process->checkTimeout(); - } - - public function testCheckTimeoutOnTerminatedProcess() - { - $process = $this->getProcess('php -v'); - $process->run(); - $process->checkTimeout(); - } - - public function testCheckTimeoutOnStartedProcess() - { - $timeout = 0.5; - $precision = 100000; - $process = $this->getProcess('php -r "sleep(3);"'); - $process->setTimeout($timeout); - $start = microtime(true); - - $process->start(); - - try { - while ($process->isRunning()) { - $process->checkTimeout(); - usleep($precision); - } - $this->fail('A RuntimeException should have been raised'); - } catch (RuntimeException $e) { - - } - $duration = microtime(true) - $start; - - $this->assertLessThan($timeout + $precision, $duration); - $this->assertFalse($process->isSuccessful()); - } - - /** - * @group idle-timeout - */ - public function testIdleTimeout() - { - $process = $this->getProcess('sleep 3'); - $process->setTimeout(10); - $process->setIdleTimeout(1); - - try { - $process->run(); - - $this->fail('A timeout exception was expected.'); - } catch (ProcessTimedOutException $ex) { - $this->assertTrue($ex->isIdleTimeout()); - $this->assertFalse($ex->isGeneralTimeout()); - $this->assertEquals(1.0, $ex->getExceededTimeout()); - } - } - - /** - * @group idle-timeout - */ - public function testIdleTimeoutNotExceededWhenOutputIsSent() - { - $process = $this->getProcess('echo "foo" && sleep 1 && echo "foo" && sleep 1 && echo "foo" && sleep 1 && echo "foo" && sleep 5'); - $process->setTimeout(5); - $process->setIdleTimeout(3); - - try { - $process->run(); - $this->fail('A timeout exception was expected.'); - } catch (ProcessTimedOutException $ex) { - $this->assertTrue($ex->isGeneralTimeout()); - $this->assertFalse($ex->isIdleTimeout()); - $this->assertEquals(5.0, $ex->getExceededTimeout()); - } - } - - public function testStartAfterATimeout() - { - $process = $this->getProcess('php -r "$n = 1000; while ($n--) {echo \'\'; usleep(1000); }"'); - $process->setTimeout(0.1); - try { - $process->run(); - $this->fail('An exception should have been raised.'); - } catch (\Exception $e) { - - } - $process->start(); - usleep(10000); - $process->stop(); - } - - public function testGetPid() - { - $process = $this->getProcess('php -r "usleep(500000);"'); - $process->start(); - $this->assertGreaterThan(0, $process->getPid()); - $process->wait(); - } - - public function testGetPidIsNullBeforeStart() - { - $process = $this->getProcess('php -r "sleep(1);"'); - $this->assertNull($process->getPid()); - } - - public function testGetPidIsNullAfterRun() - { - $process = $this->getProcess('php -m'); - $process->run(); - $this->assertNull($process->getPid()); - } - - public function testSignal() - { - $this->verifyPosixIsEnabled(); - - $process = $this->getProcess('exec php -f ' . __DIR__ . '/SignalListener.php'); - $process->start(); - usleep(500000); - $process->signal(SIGUSR1); - - while ($process->isRunning() && false === strpos($process->getoutput(), 'Caught SIGUSR1')) { - usleep(10000); - } - - $this->assertEquals('Caught SIGUSR1', $process->getOutput()); - } - - public function testExitCodeIsAvailableAfterSignal() - { - $this->verifyPosixIsEnabled(); - - $process = $this->getProcess('sleep 4'); - $process->start(); - $process->signal(SIGKILL); - - while ($process->isRunning()) { - usleep(10000); - } - - $this->assertFalse($process->isRunning()); - $this->assertTrue($process->hasBeenSignaled()); - $this->assertFalse($process->isSuccessful()); - $this->assertEquals(137, $process->getExitCode()); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\LogicException - */ - public function testSignalProcessNotRunning() - { - $this->verifyPosixIsEnabled(); - $process = $this->getProcess('php -m'); - $process->signal(SIGHUP); - } - - /** - * @dataProvider provideMethodsThatNeedARunningProcess - */ - public function testMethodsThatNeedARunningProcess($method) - { - $process = $this->getProcess('php -m'); - $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', sprintf('Process must be started before calling %s.', $method)); - call_user_func(array($process, $method)); - } - - public function provideMethodsThatNeedARunningProcess() - { - return array( - array('getOutput'), - array('getIncrementalOutput'), - array('getErrorOutput'), - array('getIncrementalErrorOutput'), - array('wait'), - ); - } - - /** - * @dataProvider provideMethodsThatNeedATerminatedProcess - */ - public function testMethodsThatNeedATerminatedProcess($method) - { - $process = $this->getProcess('php -r "sleep(1);"'); - $process->start(); - try { - call_user_func(array($process, $method)); - $process->stop(0); - $this->fail('A LogicException must have been thrown'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Process\Exception\LogicException', $e); - $this->assertEquals(sprintf('Process must be terminated before calling %s.', $method), $e->getMessage()); - } - $process->stop(0); - } - - public function provideMethodsThatNeedATerminatedProcess() - { - return array( - array('hasBeenSignaled'), - array('getTermSignal'), - array('hasBeenStopped'), - array('getStopSignal'), - ); - } - - private function verifyPosixIsEnabled() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('POSIX signals do not work on Windows'); - } - if (!defined('SIGUSR1')) { - $this->markTestSkipped('The pcntl extension is not enabled'); - } - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - */ - public function testSignalWithWrongIntSignal() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('POSIX signals do not work on Windows'); - } - - $process = $this->getProcess('php -r "sleep(3);"'); - $process->start(); - $process->signal(-4); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - */ - public function testSignalWithWrongNonIntSignal() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('POSIX signals do not work on Windows'); - } - - $process = $this->getProcess('php -r "sleep(3);"'); - $process->start(); - $process->signal('Céphalopodes'); - } - - public function responsesCodeProvider() - { - return array( - //expected output / getter / code to execute - //array(1,'getExitCode','exit(1);'), - //array(true,'isSuccessful','exit();'), - array('output', 'getOutput', 'echo \'output\';'), - ); - } - - public function pipesCodeProvider() - { - $variations = array( - 'fwrite(STDOUT, $in = file_get_contents(\'php://stdin\')); fwrite(STDERR, $in);', - 'include \''.__DIR__.'/PipeStdinInStdoutStdErrStreamSelect.php\';', - ); - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - // Avoid XL buffers on Windows because of https://bugs.php.net/bug.php?id=65650 - $sizes = array(1, 2, 4, 8); - } else { - $sizes = array(1, 16, 64, 1024, 4096); - } - - $codes = array(); - foreach ($sizes as $size) { - foreach ($variations as $code) { - $codes[] = array($code, $size); - } - } - - return $codes; - } - - /** - * provides default method names for simple getter/setter - */ - public function methodProvider() - { - $defaults = array( - array('CommandLine'), - array('Timeout'), - array('WorkingDirectory'), - array('Env'), - array('Stdin'), - array('Options') - ); - - return $defaults; - } - - /** - * @param string $commandline - * @param null $cwd - * @param array $env - * @param null $stdin - * @param int $timeout - * @param array $options - * - * @return Process - */ - abstract protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()); -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php deleted file mode 100644 index d81abf404..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/NonStopableProcess.php +++ /dev/null @@ -1,37 +0,0 @@ - (microtime(true) - $start)) { - usleep(1000); -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php deleted file mode 100644 index df48c4f55..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpExecutableFinderTest.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\PhpExecutableFinder; - -/** - * @author Robert Schönthal - */ -class PhpExecutableFinderTest extends \PHPUnit_Framework_TestCase -{ - /** - * tests find() with the env var PHP_PATH - */ - public function testFindWithPhpPath() - { - if (defined('PHP_BINARY')) { - $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4'); - } - - $f = new PhpExecutableFinder(); - - $current = $f->find(); - - //not executable PHP_PATH - putenv('PHP_PATH=/not/executable/php'); - $this->assertFalse($f->find(), '::find() returns false for not executable PHP'); - - //executable PHP_PATH - putenv('PHP_PATH='.$current); - $this->assertEquals($f->find(), $current, '::find() returns the executable PHP'); - } - - /** - * tests find() with default executable - */ - public function testFindWithSuffix() - { - if (defined('PHP_BINARY')) { - $this->markTestSkipped('The PHP binary is easily available as of PHP 5.4'); - } - - putenv('PHP_PATH='); - putenv('PHP_PEAR_PHP_BIN='); - $f = new PhpExecutableFinder(); - - $current = $f->find(); - - //TODO maybe php executable is custom or even Windows - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertTrue(is_executable($current)); - $this->assertTrue((bool) preg_match('/'.addSlashes(DIRECTORY_SEPARATOR).'php\.(exe|bat|cmd|com)$/i', $current), '::find() returns the executable PHP with suffixes'); - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php deleted file mode 100644 index df66ad624..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/PhpProcessTest.php +++ /dev/null @@ -1,29 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\PhpProcess; - -class PhpProcessTest extends \PHPUnit_Framework_TestCase -{ - public function testNonBlockingWorks() - { - $expected = 'hello world!'; - $process = new PhpProcess(<<start(); - $process->wait(); - $this->assertEquals($expected, $process->getOutput()); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php deleted file mode 100644 index cdc75255e..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/PipeStdinInStdoutStdErrStreamSelect.php +++ /dev/null @@ -1,63 +0,0 @@ - 0) { - $written = fwrite(STDOUT, (binary) $out, 32768); - if (false === $written) { - die(ERR_WRITE_FAILED); - } - $out = (binary) substr($out, $written); - } - if (null === $read && strlen($out) < 1) { - $write = array_diff($write, array(STDOUT)); - } - - if (in_array(STDERR, $w) && strlen($err) > 0) { - $written = fwrite(STDERR, (binary) $err, 32768); - if (false === $written) { - die(ERR_WRITE_FAILED); - } - $err = (binary) substr($err, $written); - } - if (null === $read && strlen($err) < 1) { - $write = array_diff($write, array(STDERR)); - } - - if ($r) { - $str = fread(STDIN, 32768); - if (false !== $str) { - $out .= $str; - $err .= $str; - } - if (false === $str || feof(STDIN)) { - $read = null; - if (!feof(STDIN)) { - die(ERR_READ_FAILED); - } - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php deleted file mode 100644 index ee14fa9cb..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessBuilderTest.php +++ /dev/null @@ -1,196 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\ProcessBuilder; - -class ProcessBuilderTest extends \PHPUnit_Framework_TestCase -{ - public function testInheritEnvironmentVars() - { - $_ENV['MY_VAR_1'] = 'foo'; - - $proc = ProcessBuilder::create() - ->add('foo') - ->getProcess(); - - unset($_ENV['MY_VAR_1']); - - $env = $proc->getEnv(); - $this->assertArrayHasKey('MY_VAR_1', $env); - $this->assertEquals('foo', $env['MY_VAR_1']); - } - - public function testAddEnvironmentVariables() - { - $pb = new ProcessBuilder(); - $env = array( - 'foo' => 'bar', - 'foo2' => 'bar2', - ); - $proc = $pb - ->add('command') - ->setEnv('foo', 'bar2') - ->addEnvironmentVariables($env) - ->inheritEnvironmentVariables(false) - ->getProcess() - ; - - $this->assertSame($env, $proc->getEnv()); - } - - public function testProcessShouldInheritAndOverrideEnvironmentVars() - { - $_ENV['MY_VAR_1'] = 'foo'; - - $proc = ProcessBuilder::create() - ->setEnv('MY_VAR_1', 'bar') - ->add('foo') - ->getProcess(); - - unset($_ENV['MY_VAR_1']); - - $env = $proc->getEnv(); - $this->assertArrayHasKey('MY_VAR_1', $env); - $this->assertEquals('bar', $env['MY_VAR_1']); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\InvalidArgumentException - */ - public function testNegativeTimeoutFromSetter() - { - $pb = new ProcessBuilder(); - $pb->setTimeout(-1); - } - - public function testNullTimeout() - { - $pb = new ProcessBuilder(); - $pb->setTimeout(10); - $pb->setTimeout(null); - - $r = new \ReflectionObject($pb); - $p = $r->getProperty('timeout'); - $p->setAccessible(true); - - $this->assertNull($p->getValue($pb)); - } - - public function testShouldSetArguments() - { - $pb = new ProcessBuilder(array('initial')); - $pb->setArguments(array('second')); - - $proc = $pb->getProcess(); - - $this->assertContains("second", $proc->getCommandLine()); - } - - public function testPrefixIsPrependedToAllGeneratedProcess() - { - $pb = new ProcessBuilder(); - $pb->setPrefix('/usr/bin/php'); - - $proc = $pb->setArguments(array('-v'))->getProcess(); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php" "-v"', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' '-v'", $proc->getCommandLine()); - } - - $proc = $pb->setArguments(array('-i'))->getProcess(); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php" "-i"', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' '-i'", $proc->getCommandLine()); - } - } - - public function testArrayPrefixesArePrependedToAllGeneratedProcess() - { - $pb = new ProcessBuilder(); - $pb->setPrefix(array('/usr/bin/php', 'composer.phar')); - - $proc = $pb->setArguments(array('-v'))->getProcess(); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php" "composer.phar" "-v"', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' 'composer.phar' '-v'", $proc->getCommandLine()); - } - - $proc = $pb->setArguments(array('-i'))->getProcess(); - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php" "composer.phar" "-i"', $proc->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php' 'composer.phar' '-i'", $proc->getCommandLine()); - } - } - - public function testShouldEscapeArguments() - { - $pb = new ProcessBuilder(array('%path%', 'foo " bar', '%baz%baz')); - $proc = $pb->getProcess(); - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertSame('^%"path"^% "foo \\" bar" "%baz%baz"', $proc->getCommandLine()); - } else { - $this->assertSame("'%path%' 'foo \" bar' '%baz%baz'", $proc->getCommandLine()); - } - } - - public function testShouldEscapeArgumentsAndPrefix() - { - $pb = new ProcessBuilder(array('arg')); - $pb->setPrefix('%prefix%'); - $proc = $pb->getProcess(); - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertSame('^%"prefix"^% "arg"', $proc->getCommandLine()); - } else { - $this->assertSame("'%prefix%' 'arg'", $proc->getCommandLine()); - } - } - - /** - * @expectedException \Symfony\Component\Process\Exception\LogicException - */ - public function testShouldThrowALogicExceptionIfNoPrefixAndNoArgument() - { - ProcessBuilder::create()->getProcess(); - } - - public function testShouldNotThrowALogicExceptionIfNoArgument() - { - $process = ProcessBuilder::create() - ->setPrefix('/usr/bin/php') - ->getProcess(); - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); - } - } - - public function testShouldNotThrowALogicExceptionIfNoPrefix() - { - $process = ProcessBuilder::create(array('/usr/bin/php')) - ->getProcess(); - - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->assertEquals('"/usr/bin/php"', $process->getCommandLine()); - } else { - $this->assertEquals("'/usr/bin/php'", $process->getCommandLine()); - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php deleted file mode 100644 index 5da55e75e..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessFailedExceptionTest.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\Exception\ProcessFailedException; - -/** - * @author Sebastian Marek - */ -class ProcessFailedExceptionTest extends \PHPUnit_Framework_TestCase -{ - /** - * tests ProcessFailedException throws exception if the process was successful - */ - public function testProcessFailedExceptionThrowsException() - { - $process = $this->getMock( - 'Symfony\Component\Process\Process', - array('isSuccessful'), - array('php') - ); - $process->expects($this->once()) - ->method('isSuccessful') - ->will($this->returnValue(true)); - - $this->setExpectedException( - '\InvalidArgumentException', - 'Expected a failed process, but the given process was successful.' - ); - - new ProcessFailedException($process); - } - - /** - * tests ProcessFailedException uses information from process output - * to generate exception message - */ - public function testProcessFailedExceptionPopulatesInformationFromProcessOutput() - { - $cmd = 'php'; - $exitCode = 1; - $exitText = 'General error'; - $output = "Command output"; - $errorOutput = "FATAL: Unexpected error"; - - $process = $this->getMock( - 'Symfony\Component\Process\Process', - array('isSuccessful', 'getOutput', 'getErrorOutput', 'getExitCode', 'getExitCodeText'), - array($cmd) - ); - $process->expects($this->once()) - ->method('isSuccessful') - ->will($this->returnValue(false)); - $process->expects($this->once()) - ->method('getOutput') - ->will($this->returnValue($output)); - $process->expects($this->once()) - ->method('getErrorOutput') - ->will($this->returnValue($errorOutput)); - $process->expects($this->once()) - ->method('getExitCode') - ->will($this->returnValue($exitCode)); - $process->expects($this->once()) - ->method('getExitCodeText') - ->will($this->returnValue($exitText)); - - $exception = new ProcessFailedException($process); - - $this->assertEquals( - "The command \"$cmd\" failed.\nExit Code: $exitCode($exitText)\n\nOutput:\n================\n{$output}\n\nError Output:\n================\n{$errorOutput}", - $exception->getMessage() - ); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php deleted file mode 100644 index 3977bcdcf..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessInSigchildEnvironment.php +++ /dev/null @@ -1,22 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\Process; - -class ProcessInSigchildEnvironment extends Process -{ - protected function isSigchildEnabled() - { - return true; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php deleted file mode 100644 index 8ba94c114..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/ProcessUtilsTest.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\ProcessUtils; - -class ProcessUtilsTest extends \PHPUnit_Framework_TestCase -{ - /** - * @dataProvider dataArguments - */ - public function testEscapeArgument($result, $argument) - { - $this->assertSame($result, ProcessUtils::escapeArgument($argument)); - } - - public function dataArguments() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - return array( - array('"\"php\" \"-v\""', '"php" "-v"'), - array('"foo bar"', 'foo bar'), - array('^%"path"^%', '%path%'), - array('"<|>\\" \\"\'f"', '<|>" "\'f'), - array('""', ''), - array('"with\trailingbs\\\\"', 'with\trailingbs\\'), - ); - } - - return array( - array("'\"php\" \"-v\"'", '"php" "-v"'), - array("'foo bar'", 'foo bar'), - array("'%path%'", '%path%'), - array("'<|>\" \"'\\''f'", '<|>" "\'f'), - array("''", ''), - array("'with\\trailingbs\\'", 'with\trailingbs\\'), - ); - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php deleted file mode 100644 index 798e66a57..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildDisabledProcessTest.php +++ /dev/null @@ -1,224 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -class SigchildDisabledProcessTest extends AbstractProcessTest -{ - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testGetExitCode() - { - parent::testGetExitCode(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testGetExitCodeIsNullOnStart() - { - parent::testGetExitCodeIsNullOnStart(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testGetExitCodeIsNullOnWhenStartingAgain() - { - parent::testGetExitCodeIsNullOnWhenStartingAgain(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testExitCodeCommandFailed() - { - parent::testExitCodeCommandFailed(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessIsSignaledIfStopped() - { - parent::testProcessIsSignaledIfStopped(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithTermSignal() - { - parent::testProcessWithTermSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessIsNotSignaled() - { - parent::testProcessIsNotSignaled(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage his PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithoutTermSignal() - { - parent::testProcessWithoutTermSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testCheckTimeoutOnStartedProcess() - { - parent::testCheckTimeoutOnStartedProcess(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPid() - { - parent::testGetPid(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPidIsNullBeforeStart() - { - parent::testGetPidIsNullBeforeStart(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPidIsNullAfterRun() - { - parent::testGetPidIsNullAfterRun(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testExitCodeText() - { - $process = $this->getProcess('qdfsmfkqsdfmqmsd'); - $process->run(); - - $process->getExitCodeText(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testExitCodeTextIsNullWhenExitCodeIsNull() - { - parent::testExitCodeTextIsNullWhenExitCodeIsNull(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testIsSuccessful() - { - parent::testIsSuccessful(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testIsSuccessfulOnlyAfterTerminated() - { - parent::testIsSuccessfulOnlyAfterTerminated(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testIsNotSuccessful() - { - parent::testIsNotSuccessful(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. You must use setEnhanceSigchildCompatibility() to use this method. - */ - public function testTTYCommandExitCode() - { - parent::testTTYCommandExitCode(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled. - */ - public function testSignal() - { - parent::testSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithoutTermSignalIsNotSignaled() - { - parent::testProcessWithoutTermSignalIsNotSignaled(); - } - - public function testStopWithTimeoutIsActuallyWorking() - { - $this->markTestSkipped('Stopping with signal is not supported in sigchild environment'); - } - - public function testProcessThrowsExceptionWhenExternallySignaled() - { - $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); - } - - public function testExitCodeIsAvailableAfterSignal() - { - $this->markTestSkipped('Signal is not supported in sigchild environment'); - } - - /** - * {@inheritdoc} - */ - protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) - { - $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options); - $process->setEnhanceSigchildCompatibility(false); - - return $process; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php deleted file mode 100644 index 65dd4bb57..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/SigchildEnabledProcessTest.php +++ /dev/null @@ -1,133 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -class SigchildEnabledProcessTest extends AbstractProcessTest -{ - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessIsSignaledIfStopped() - { - parent::testProcessIsSignaledIfStopped(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithTermSignal() - { - parent::testProcessWithTermSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessIsNotSignaled() - { - parent::testProcessIsNotSignaled(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithoutTermSignal() - { - parent::testProcessWithoutTermSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPid() - { - parent::testGetPid(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPidIsNullBeforeStart() - { - parent::testGetPidIsNullBeforeStart(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process identifier can not be retrieved. - */ - public function testGetPidIsNullAfterRun() - { - parent::testGetPidIsNullAfterRun(); - } - - public function testExitCodeText() - { - $process = $this->getProcess('qdfsmfkqsdfmqmsd'); - $process->run(); - - $this->assertInternalType('string', $process->getExitCodeText()); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. The process can not be signaled. - */ - public function testSignal() - { - parent::testSignal(); - } - - /** - * @expectedException \Symfony\Component\Process\Exception\RuntimeException - * @expectedExceptionMessage This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved. - */ - public function testProcessWithoutTermSignalIsNotSignaled() - { - parent::testProcessWithoutTermSignalIsNotSignaled(); - } - - public function testProcessThrowsExceptionWhenExternallySignaled() - { - $this->markTestSkipped('Retrieving Pid is not supported in sigchild environment'); - } - - public function testExitCodeIsAvailableAfterSignal() - { - $this->markTestSkipped('Signal is not supported in sigchild environment'); - } - - public function testStartAfterATimeout() - { - if (defined('PHP_WINDOWS_VERSION_BUILD')) { - $this->markTestSkipped('Restarting a timed-out process on Windows is not supported in sigchild environment'); - } - parent::testStartAfterATimeout(); - } - - /** - * {@inheritdoc} - */ - protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) - { - $process = new ProcessInSigchildEnvironment($commandline, $cwd, $env, $stdin, $timeout, $options); - $process->setEnhanceSigchildCompatibility(true); - - return $process; - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php b/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php deleted file mode 100644 index 143515d4d..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/Tests/SignalListener.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Process\Tests; - -use Symfony\Component\Process\Process; - -class SimpleProcessTest extends AbstractProcessTest -{ - private $enabledSigchild = false; - - public function setUp() - { - ob_start(); - phpinfo(INFO_GENERAL); - - $this->enabledSigchild = false !== strpos(ob_get_clean(), '--enable-sigchild'); - } - - public function testGetExitCode() - { - $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case - parent::testGetExitCode(); - } - - public function testExitCodeCommandFailed() - { - $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case - parent::testExitCodeCommandFailed(); - } - - public function testProcessIsSignaledIfStopped() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); - parent::testProcessIsSignaledIfStopped(); - } - - public function testProcessWithTermSignal() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); - parent::testProcessWithTermSignal(); - } - - public function testProcessIsNotSignaled() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); - parent::testProcessIsNotSignaled(); - } - - public function testProcessWithoutTermSignal() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); - parent::testProcessWithoutTermSignal(); - } - - public function testExitCodeText() - { - $this->skipIfPHPSigchild(); // This test use exitcode that is not available in this case - parent::testExitCodeText(); - } - - public function testIsSuccessful() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testIsSuccessful(); - } - - public function testIsNotSuccessful() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testIsNotSuccessful(); - } - - public function testGetPid() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testGetPid(); - } - - public function testGetPidIsNullBeforeStart() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testGetPidIsNullBeforeStart(); - } - - public function testGetPidIsNullAfterRun() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testGetPidIsNullAfterRun(); - } - - public function testSignal() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - parent::testSignal(); - } - - public function testProcessWithoutTermSignalIsNotSignaled() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. Term signal can not be retrieved'); - parent::testProcessWithoutTermSignalIsNotSignaled(); - } - - public function testProcessThrowsExceptionWhenExternallySignaled() - { - $this->skipIfPHPSigchild(); // This test use PID that is not available in this case - parent::testProcessThrowsExceptionWhenExternallySignaled(); - } - - public function testExitCodeIsAvailableAfterSignal() - { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - parent::testExitCodeIsAvailableAfterSignal(); - } - - public function testSignalProcessNotRunning() - { - $this->setExpectedException('Symfony\Component\Process\Exception\LogicException', 'Can not send signal on a non running process.'); - parent::testSignalProcessNotRunning(); - } - - public function testSignalWithWrongIntSignal() - { - if ($this->enabledSigchild) { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - } else { - $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `-4`.'); - } - parent::testSignalWithWrongIntSignal(); - } - - public function testSignalWithWrongNonIntSignal() - { - if ($this->enabledSigchild) { - $this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.'); - } else { - $this->setExpectedException('Symfony\Component\Process\Exception\RuntimeException', 'Error while sending signal `Céphalopodes`.'); - } - parent::testSignalWithWrongNonIntSignal(); - } - - /** - * {@inheritdoc} - */ - protected function getProcess($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array()) - { - return new Process($commandline, $cwd, $env, $stdin, $timeout, $options); - } - - private function skipIfPHPSigchild() - { - if ($this->enabledSigchild) { - $this->markTestSkipped('Your PHP has been compiled with --enable-sigchild, this test can not be executed'); - } - } - - private function expectExceptionIfPHPSigchild($classname, $message) - { - if ($this->enabledSigchild) { - $this->setExpectedException($classname, $message); - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/composer.json b/core/lib/symfony/process/Symfony/Component/Process/composer.json deleted file mode 100644 index a05735b8c..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "symfony/process", - "type": "library", - "description": "Symfony Process Component", - "keywords": [], - "homepage": "http://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.3.3" - }, - "autoload": { - "psr-0": { "Symfony\\Component\\Process\\": "" } - }, - "target-dir": "Symfony/Component/Process", - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - } -} diff --git a/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist b/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist deleted file mode 100644 index 9d5830f9e..000000000 --- a/core/lib/symfony/process/Symfony/Component/Process/phpunit.xml.dist +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - ./Tests/ - - - - - - ./ - - ./Tests - - - - diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore b/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore deleted file mode 100644 index c49a5d8df..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -vendor/ -composer.lock -phpunit.xml diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md b/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md deleted file mode 100644 index 096cf654d..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/CHANGELOG.md +++ /dev/null @@ -1,8 +0,0 @@ -CHANGELOG -========= - -2.1.0 ------ - - * Yaml::parse() does not evaluate loaded files as PHP files by default - anymore (call Yaml::enablePhpParsing() to get back the old behavior) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php deleted file mode 100644 index 26103c88e..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Dumper.php +++ /dev/null @@ -1,73 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -/** - * Dumper dumps PHP variables to YAML strings. - * - * @author Fabien Potencier - */ -class Dumper -{ - /** - * The amount of spaces to use for indentation of nested nodes. - * - * @var int - */ - protected $indentation = 4; - - /** - * Sets the indentation. - * - * @param int $num The amount of spaces to use for indentation of nested nodes. - */ - public function setIndentation($num) - { - $this->indentation = (int) $num; - } - - /** - * Dumps a PHP value to YAML. - * - * @param mixed $input The PHP value - * @param int $inline The level where you switch to inline YAML - * @param int $indent The level of indentation (used internally) - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return string The YAML representation of the PHP value - */ - public function dump($input, $inline = 0, $indent = 0, $exceptionOnInvalidType = false, $objectSupport = false) - { - $output = ''; - $prefix = $indent ? str_repeat(' ', $indent) : ''; - - if ($inline <= 0 || !is_array($input) || empty($input)) { - $output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport); - } else { - $isAHash = array_keys($input) !== range(0, count($input) - 1); - - foreach ($input as $key => $value) { - $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); - - $output .= sprintf('%s%s%s%s', - $prefix, - $isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-', - $willBeInlined ? ' ' : "\n", - $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $exceptionOnInvalidType, $objectSupport) - ).($willBeInlined ? "\n" : ''); - } - } - - return $output; - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php deleted file mode 100644 index 4a6b62161..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Escaper.php +++ /dev/null @@ -1,89 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -/** - * Escaper encapsulates escaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - */ -class Escaper -{ - // Characters that would cause a dumped string to require double quoting. - const REGEX_CHARACTER_TO_ESCAPE = "[\\x00-\\x1f]|\xc2\x85|\xc2\xa0|\xe2\x80\xa8|\xe2\x80\xa9"; - - // Mapping arrays for escaping a double quoted string. The backslash is - // first to ensure proper escaping because str_replace operates iteratively - // on the input arrays. This ordering of the characters avoids the use of strtr, - // which performs more slowly. - private static $escapees = array('\\\\', '\\"', '"', - "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", - "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", - "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", - "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", - "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9"); - private static $escaped = array('\\"', '\\\\', '\\"', - "\\0", "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a", - "\\b", "\\t", "\\n", "\\v", "\\f", "\\r", "\\x0e", "\\x0f", - "\\x10", "\\x11", "\\x12", "\\x13", "\\x14", "\\x15", "\\x16", "\\x17", - "\\x18", "\\x19", "\\x1a", "\\e", "\\x1c", "\\x1d", "\\x1e", "\\x1f", - "\\N", "\\_", "\\L", "\\P"); - - /** - * Determines if a PHP value would require double quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require double quotes. - */ - public static function requiresDoubleQuoting($value) - { - return preg_match('/'.self::REGEX_CHARACTER_TO_ESCAPE.'/u', $value); - } - - /** - * Escapes and surrounds a PHP value with double quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithDoubleQuotes($value) - { - return sprintf('"%s"', str_replace(self::$escapees, self::$escaped, $value)); - } - - /** - * Determines if a PHP value would require single quoting in YAML. - * - * @param string $value A PHP value - * - * @return bool True if the value would require single quotes. - */ - public static function requiresSingleQuoting($value) - { - return preg_match('/[ \s \' " \: \{ \} \[ \] , & \* \# \?] | \A[ \- ? | < > = ! % @ ` ]/x', $value); - } - - /** - * Escapes and surrounds a PHP value with single quotes. - * - * @param string $value A PHP value - * - * @return string The quoted, escaped string - */ - public static function escapeWithSingleQuotes($value) - { - return sprintf("'%s'", str_replace('\'', '\'\'', $value)); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php deleted file mode 100644 index 9b3e6de07..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/DumpException.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during dumping. - * - * @author Fabien Potencier - * - * @api - */ -class DumpException extends RuntimeException -{ -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php deleted file mode 100644 index 92e5c2ea4..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ExceptionInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception interface for all exceptions thrown by the component. - * - * @author Fabien Potencier - * - * @api - */ -interface ExceptionInterface -{ -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php deleted file mode 100644 index ff01d6b9a..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/ParseException.php +++ /dev/null @@ -1,148 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -if (!defined('JSON_UNESCAPED_UNICODE')) { - define('JSON_UNESCAPED_SLASHES', 64); - define('JSON_UNESCAPED_UNICODE', 256); -} - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Fabien Potencier - * - * @api - */ -class ParseException extends RuntimeException -{ - private $parsedFile; - private $parsedLine; - private $snippet; - private $rawMessage; - - /** - * Constructor. - * - * @param string $message The error message - * @param int $parsedLine The line where the error occurred - * @param int $snippet The snippet of code near the problem - * @param string $parsedFile The file name where the error occurred - * @param \Exception $previous The previous exception - */ - public function __construct($message, $parsedLine = -1, $snippet = null, $parsedFile = null, \Exception $previous = null) - { - $this->parsedFile = $parsedFile; - $this->parsedLine = $parsedLine; - $this->snippet = $snippet; - $this->rawMessage = $message; - - $this->updateRepr(); - - parent::__construct($this->message, 0, $previous); - } - - /** - * Gets the snippet of code near the error. - * - * @return string The snippet of code - */ - public function getSnippet() - { - return $this->snippet; - } - - /** - * Sets the snippet of code near the error. - * - * @param string $snippet The code snippet - */ - public function setSnippet($snippet) - { - $this->snippet = $snippet; - - $this->updateRepr(); - } - - /** - * Gets the filename where the error occurred. - * - * This method returns null if a string is parsed. - * - * @return string The filename - */ - public function getParsedFile() - { - return $this->parsedFile; - } - - /** - * Sets the filename where the error occurred. - * - * @param string $parsedFile The filename - */ - public function setParsedFile($parsedFile) - { - $this->parsedFile = $parsedFile; - - $this->updateRepr(); - } - - /** - * Gets the line where the error occurred. - * - * @return int The file line - */ - public function getParsedLine() - { - return $this->parsedLine; - } - - /** - * Sets the line where the error occurred. - * - * @param int $parsedLine The file line - */ - public function setParsedLine($parsedLine) - { - $this->parsedLine = $parsedLine; - - $this->updateRepr(); - } - - private function updateRepr() - { - $this->message = $this->rawMessage; - - $dot = false; - if ('.' === substr($this->message, -1)) { - $this->message = substr($this->message, 0, -1); - $dot = true; - } - - if (null !== $this->parsedFile) { - $this->message .= sprintf(' in %s', json_encode($this->parsedFile, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); - } - - if ($this->parsedLine >= 0) { - $this->message .= sprintf(' at line %d', $this->parsedLine); - } - - if ($this->snippet) { - $this->message .= sprintf(' (near "%s")', $this->snippet); - } - - if ($dot) { - $this->message .= '.'; - } - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php deleted file mode 100644 index 3573bf15a..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Exception/RuntimeException.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Exception; - -/** - * Exception class thrown when an error occurs during parsing. - * - * @author Romain Neutron - * - * @api - */ -class RuntimeException extends \RuntimeException implements ExceptionInterface -{ -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php deleted file mode 100644 index b0d6a031f..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Inline.php +++ /dev/null @@ -1,469 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; -use Symfony\Component\Yaml\Exception\DumpException; - -/** - * Inline implements a YAML parser/dumper for the YAML inline syntax. - * - * @author Fabien Potencier - */ -class Inline -{ - const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')'; - - private static $exceptionOnInvalidType = false; - private static $objectSupport = false; - - /** - * Converts a YAML string to a PHP array. - * - * @param string $value A YAML string - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return array A PHP array representing the YAML string - * - * @throws ParseException - */ - public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false) - { - self::$exceptionOnInvalidType = $exceptionOnInvalidType; - self::$objectSupport = $objectSupport; - - $value = trim($value); - - if (0 == strlen($value)) { - return ''; - } - - if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('ASCII'); - } - - $i = 0; - switch ($value[0]) { - case '[': - $result = self::parseSequence($value, $i); - ++$i; - break; - case '{': - $result = self::parseMapping($value, $i); - ++$i; - break; - default: - $result = self::parseScalar($value, null, array('"', "'"), $i); - } - - // some comments are allowed at the end - if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) { - throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i))); - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return $result; - } - - /** - * Dumps a given PHP variable to a YAML string. - * - * @param mixed $value The PHP variable to convert - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return string The YAML string representing the PHP array - * - * @throws DumpException When trying to dump PHP resource - */ - public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false) - { - switch (true) { - case is_resource($value): - if ($exceptionOnInvalidType) { - throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value))); - } - - return 'null'; - case is_object($value): - if ($objectSupport) { - return '!!php/object:'.serialize($value); - } - - if ($exceptionOnInvalidType) { - throw new DumpException('Object support when dumping a YAML file has been disabled.'); - } - - return 'null'; - case is_array($value): - return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport); - case null === $value: - return 'null'; - case true === $value: - return 'true'; - case false === $value: - return 'false'; - case ctype_digit($value): - return is_string($value) ? "'$value'" : (int) $value; - case is_numeric($value): - $locale = setlocale(LC_NUMERIC, 0); - if (false !== $locale) { - setlocale(LC_NUMERIC, 'C'); - } - $repr = is_string($value) ? "'$value'" : (is_infinite($value) ? str_ireplace('INF', '.Inf', strval($value)) : strval($value)); - - if (false !== $locale) { - setlocale(LC_NUMERIC, $locale); - } - - return $repr; - case Escaper::requiresDoubleQuoting($value): - return Escaper::escapeWithDoubleQuotes($value); - case Escaper::requiresSingleQuoting($value): - return Escaper::escapeWithSingleQuotes($value); - case '' == $value: - return "''"; - case preg_match(self::getTimestampRegex(), $value): - case in_array(strtolower($value), array('null', '~', 'true', 'false')): - return "'$value'"; - default: - return $value; - } - } - - /** - * Dumps a PHP array to a YAML string. - * - * @param array $value The PHP array to dump - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return string The YAML string representing the PHP array - */ - private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport) - { - // array - $keys = array_keys($value); - if ((1 == count($keys) && '0' == $keys[0]) - || (count($keys) > 1 && array_reduce($keys, function ($v, $w) { return (int) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2) - ) { - $output = array(); - foreach ($value as $val) { - $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); - } - - return sprintf('[%s]', implode(', ', $output)); - } - - // mapping - $output = array(); - foreach ($value as $key => $val) { - $output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport)); - } - - return sprintf('{ %s }', implode(', ', $output)); - } - - /** - * Parses a scalar to a YAML string. - * - * @param scalar $scalar - * @param string $delimiters - * @param array $stringDelimiters - * @param int &$i - * @param bool $evaluate - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true) - { - if (in_array($scalar[$i], $stringDelimiters)) { - // quoted scalar - $output = self::parseQuotedScalar($scalar, $i); - - if (null !== $delimiters) { - $tmp = ltrim(substr($scalar, $i), ' '); - if (!in_array($tmp[0], $delimiters)) { - throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i))); - } - } - } else { - // "normal" string - if (!$delimiters) { - $output = substr($scalar, $i); - $i += strlen($output); - - // remove comments - if (false !== $strpos = strpos($output, ' #')) { - $output = rtrim(substr($output, 0, $strpos)); - } - } elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) { - $output = $match[1]; - $i += strlen($output); - } else { - throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar)); - } - - if ($evaluate) { - $output = self::evaluateScalar($output); - } - } - - return $output; - } - - /** - * Parses a quoted scalar to YAML. - * - * @param string $scalar - * @param int &$i - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseQuotedScalar($scalar, &$i) - { - if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { - throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i))); - } - - $output = substr($match[0], 1, strlen($match[0]) - 2); - - $unescaper = new Unescaper(); - if ('"' == $scalar[$i]) { - $output = $unescaper->unescapeDoubleQuotedString($output); - } else { - $output = $unescaper->unescapeSingleQuotedString($output); - } - - $i += strlen($match[0]); - - return $output; - } - - /** - * Parses a sequence to a YAML string. - * - * @param string $sequence - * @param int &$i - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseSequence($sequence, &$i = 0) - { - $output = array(); - $len = strlen($sequence); - $i += 1; - - // [foo, bar, ...] - while ($i < $len) { - switch ($sequence[$i]) { - case '[': - // nested sequence - $output[] = self::parseSequence($sequence, $i); - break; - case '{': - // nested mapping - $output[] = self::parseMapping($sequence, $i); - break; - case ']': - return $output; - case ',': - case ' ': - break; - default: - $isQuoted = in_array($sequence[$i], array('"', "'")); - $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i); - - if (!$isQuoted && false !== strpos($value, ': ')) { - // embedded mapping? - try { - $value = self::parseMapping('{'.$value.'}'); - } catch (\InvalidArgumentException $e) { - // no, it's not - } - } - - $output[] = $value; - - --$i; - } - - ++$i; - } - - throw new ParseException(sprintf('Malformed inline YAML string %s', $sequence)); - } - - /** - * Parses a mapping to a YAML string. - * - * @param string $mapping - * @param int &$i - * - * @return string A YAML string - * - * @throws ParseException When malformed inline YAML string is parsed - */ - private static function parseMapping($mapping, &$i = 0) - { - $output = array(); - $len = strlen($mapping); - $i += 1; - - // {foo: bar, bar:foo, ...} - while ($i < $len) { - switch ($mapping[$i]) { - case ' ': - case ',': - ++$i; - continue 2; - case '}': - return $output; - } - - // key - $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false); - - // value - $done = false; - while ($i < $len) { - switch ($mapping[$i]) { - case '[': - // nested sequence - $output[$key] = self::parseSequence($mapping, $i); - $done = true; - break; - case '{': - // nested mapping - $output[$key] = self::parseMapping($mapping, $i); - $done = true; - break; - case ':': - case ' ': - break; - default: - $output[$key] = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i); - $done = true; - --$i; - } - - ++$i; - - if ($done) { - continue 2; - } - } - } - - throw new ParseException(sprintf('Malformed inline YAML string %s', $mapping)); - } - - /** - * Evaluates scalars and replaces magic values. - * - * @param string $scalar - * - * @return string A YAML string - */ - private static function evaluateScalar($scalar) - { - $scalar = trim($scalar); - $scalarLower = strtolower($scalar); - switch (true) { - case 'null' === $scalarLower: - case '' === $scalar: - case '~' === $scalar: - return; - case 'true' === $scalarLower: - return true; - case 'false' === $scalarLower: - return false; - // Optimise for returning strings. - case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]): - switch (true) { - case 0 === strpos($scalar, '!str'): - return (string) substr($scalar, 5); - case 0 === strpos($scalar, '! '): - return intval(self::parseScalar(substr($scalar, 2))); - case 0 === strpos($scalar, '!!php/object:'): - if (self::$objectSupport) { - return unserialize(substr($scalar, 13)); - } - - if (self::$exceptionOnInvalidType) { - throw new ParseException('Object support when parsing a YAML file has been disabled.'); - } - - return; - case ctype_digit($scalar): - $raw = $scalar; - $cast = intval($scalar); - - return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); - case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)): - $raw = $scalar; - $cast = intval($scalar); - - return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw); - case is_numeric($scalar): - return '0x' == $scalar[0].$scalar[1] ? hexdec($scalar) : floatval($scalar); - case '.inf' === $scalarLower: - case '.nan' === $scalarLower: - return -log(0); - case '-.inf' === $scalarLower: - return log(0); - case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar): - return floatval(str_replace(',', '', $scalar)); - case preg_match(self::getTimestampRegex(), $scalar): - return strtotime($scalar); - } - default: - return (string) $scalar; - } - } - - /** - * Gets a regex that matches a YAML date. - * - * @return string The regular expression - * - * @see http://www.yaml.org/spec/1.2/spec.html#id2761573 - */ - private static function getTimestampRegex() - { - return <<[0-9][0-9][0-9][0-9]) - -(?P[0-9][0-9]?) - -(?P[0-9][0-9]?) - (?:(?:[Tt]|[ \t]+) - (?P[0-9][0-9]?) - :(?P[0-9][0-9]) - :(?P[0-9][0-9]) - (?:\.(?P[0-9]*))? - (?:[ \t]*(?PZ|(?P[-+])(?P[0-9][0-9]?) - (?::(?P[0-9][0-9]))?))?)? - $~x -EOF; - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE b/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE deleted file mode 100644 index 0b3292cf9..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2004-2014 Fabien Potencier - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php deleted file mode 100644 index d4e0a9fa6..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Parser.php +++ /dev/null @@ -1,638 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; - -/** - * Parser parses YAML strings to convert them to PHP arrays. - * - * @author Fabien Potencier - */ -class Parser -{ - const FOLDED_SCALAR_PATTERN = '(?P\||>)(?P\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P +#.*)?'; - - private $offset = 0; - private $lines = array(); - private $currentLineNb = -1; - private $currentLine = ''; - private $refs = array(); - - /** - * Constructor - * - * @param int $offset The offset of YAML document (used for line numbers in error messages) - */ - public function __construct($offset = 0) - { - $this->offset = $offset; - } - - /** - * Parses a YAML string to a PHP value. - * - * @param string $value A YAML string - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return mixed A PHP value - * - * @throws ParseException If the YAML is not valid - */ - public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false) - { - $this->currentLineNb = -1; - $this->currentLine = ''; - $this->lines = explode("\n", $this->cleanup($value)); - - if (function_exists('mb_detect_encoding') && false === mb_detect_encoding($value, 'UTF-8', true)) { - throw new ParseException('The YAML value does not appear to be valid UTF-8.'); - } - - if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) { - $mbEncoding = mb_internal_encoding(); - mb_internal_encoding('UTF-8'); - } - - $data = array(); - $context = null; - while ($this->moveToNextLine()) { - if ($this->isCurrentLineEmpty()) { - continue; - } - - // tab? - if ("\t" === $this->currentLine[0]) { - throw new ParseException('A YAML file cannot contain tabs as indentation.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - $isRef = $isInPlace = $isProcessed = false; - if (preg_match('#^\-((?P\s+)(?P.+?))?\s*$#u', $this->currentLine, $values)) { - if ($context && 'mapping' == $context) { - throw new ParseException('You cannot define a sequence item when in a mapping'); - } - $context = 'sequence'; - - if (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - // array - if (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - $c = $this->getRealCurrentLineNb() + 1; - $parser = new Parser($c); - $parser->refs =& $this->refs; - $data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); - } else { - if (isset($values['leadspaces']) - && ' ' == $values['leadspaces'] - && preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P.+?))?\s*$#u', $values['value'], $matches) - ) { - // this is a compact notation element, add to next block and parse - $c = $this->getRealCurrentLineNb(); - $parser = new Parser($c); - $parser->refs =& $this->refs; - - $block = $values['value']; - if ($this->isNextLineIndented()) { - $block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2); - } - - $data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport); - } else { - $data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); - } - } - } elseif (preg_match('#^(?P'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values) && false === strpos($values['key'],' #')) { - if ($context && 'sequence' == $context) { - throw new ParseException('You cannot define a mapping item when in a sequence'); - } - $context = 'mapping'; - - // force correct settings - Inline::parse(null, $exceptionOnInvalidType, $objectSupport); - try { - $key = Inline::parseScalar($values['key']); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - if ('<<' === $key) { - if (isset($values['value']) && 0 === strpos($values['value'], '*')) { - $isInPlace = substr($values['value'], 1); - if (!array_key_exists($isInPlace, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $isInPlace), $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } else { - if (isset($values['value']) && $values['value'] !== '') { - $value = $values['value']; - } else { - $value = $this->getNextEmbedBlock(); - } - $c = $this->getRealCurrentLineNb() + 1; - $parser = new Parser($c); - $parser->refs =& $this->refs; - $parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport); - - $merged = array(); - if (!is_array($parsed)) { - throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } elseif (isset($parsed[0])) { - // Numeric array, merge individual elements - foreach (array_reverse($parsed) as $parsedItem) { - if (!is_array($parsedItem)) { - throw new ParseException('Merge items must be arrays.', $this->getRealCurrentLineNb() + 1, $parsedItem); - } - $merged = array_merge($parsedItem, $merged); - } - } else { - // Associative array, merge - $merged = array_merge($merged, $parsed); - } - - $isProcessed = $merged; - } - } elseif (isset($values['value']) && preg_match('#^&(?P[^ ]+) *(?P.*)#u', $values['value'], $matches)) { - $isRef = $matches['ref']; - $values['value'] = $matches['value']; - } - - if ($isProcessed) { - // Merge keys - $data = $isProcessed; - // hash - } elseif (!isset($values['value']) || '' == trim($values['value'], ' ') || 0 === strpos(ltrim($values['value'], ' '), '#')) { - // if next line is less indented or equal, then it means that the current value is null - if (!$this->isNextLineIndented() && !$this->isNextLineUnIndentedCollection()) { - $data[$key] = null; - } else { - $c = $this->getRealCurrentLineNb() + 1; - $parser = new Parser($c); - $parser->refs =& $this->refs; - $data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport); - } - } else { - if ($isInPlace) { - $data = $this->refs[$isInPlace]; - } else { - $data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport); - } - } - } else { - // 1-liner optionally followed by newline - $lineCount = count($this->lines); - if (1 === $lineCount || (2 === $lineCount && empty($this->lines[1]))) { - try { - $value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - - if (is_array($value)) { - $first = reset($value); - if (is_string($first) && 0 === strpos($first, '*')) { - $data = array(); - foreach ($value as $alias) { - $data[] = $this->refs[substr($alias, 1)]; - } - $value = $data; - } - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return $value; - } - - switch (preg_last_error()) { - case PREG_INTERNAL_ERROR: - $error = 'Internal PCRE error.'; - break; - case PREG_BACKTRACK_LIMIT_ERROR: - $error = 'pcre.backtrack_limit reached.'; - break; - case PREG_RECURSION_LIMIT_ERROR: - $error = 'pcre.recursion_limit reached.'; - break; - case PREG_BAD_UTF8_ERROR: - $error = 'Malformed UTF-8 data.'; - break; - case PREG_BAD_UTF8_OFFSET_ERROR: - $error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.'; - break; - default: - $error = 'Unable to parse.'; - } - - throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - - if ($isRef) { - $this->refs[$isRef] = end($data); - } - } - - if (isset($mbEncoding)) { - mb_internal_encoding($mbEncoding); - } - - return empty($data) ? null : $data; - } - - /** - * Returns the current line number (takes the offset into account). - * - * @return int The current line number - */ - private function getRealCurrentLineNb() - { - return $this->currentLineNb + $this->offset; - } - - /** - * Returns the current line indentation. - * - * @return int The current line indentation - */ - private function getCurrentLineIndentation() - { - return strlen($this->currentLine) - strlen(ltrim($this->currentLine, ' ')); - } - - /** - * Returns the next embed block of YAML. - * - * @param int $indentation The indent level at which the block is to be read, or null for default - * - * @return string A YAML string - * - * @throws ParseException When indentation problem are detected - */ - private function getNextEmbedBlock($indentation = null) - { - $this->moveToNextLine(); - - if (null === $indentation) { - $newIndent = $this->getCurrentLineIndentation(); - - $unindentedEmbedBlock = $this->isStringUnIndentedCollectionItem($this->currentLine); - - if (!$this->isCurrentLineEmpty() && 0 === $newIndent && !$unindentedEmbedBlock) { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } else { - $newIndent = $indentation; - } - - $data = array(substr($this->currentLine, $newIndent)); - - $isItUnindentedCollection = $this->isStringUnIndentedCollectionItem($this->currentLine); - - // Comments must not be removed inside a string block (ie. after a line ending with "|") - $removeCommentsPattern = '~'.self::FOLDED_SCALAR_PATTERN.'$~'; - $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); - - while ($this->moveToNextLine()) { - $indent = $this->getCurrentLineIndentation(); - - if ($indent === $newIndent) { - $removeComments = !preg_match($removeCommentsPattern, $this->currentLine); - } - - if ($isItUnindentedCollection && !$this->isStringUnIndentedCollectionItem($this->currentLine)) { - $this->moveToPreviousLine(); - break; - } - - if ($this->isCurrentLineBlank()) { - $data[] = substr($this->currentLine, $newIndent); - continue; - } - - if ($removeComments && $this->isCurrentLineComment()) { - continue; - } - - if ($indent >= $newIndent) { - $data[] = substr($this->currentLine, $newIndent); - } elseif (0 == $indent) { - $this->moveToPreviousLine(); - - break; - } else { - throw new ParseException('Indentation problem.', $this->getRealCurrentLineNb() + 1, $this->currentLine); - } - } - - return implode("\n", $data); - } - - /** - * Moves the parser to the next line. - * - * @return bool - */ - private function moveToNextLine() - { - if ($this->currentLineNb >= count($this->lines) - 1) { - return false; - } - - $this->currentLine = $this->lines[++$this->currentLineNb]; - - return true; - } - - /** - * Moves the parser to the previous line. - */ - private function moveToPreviousLine() - { - $this->currentLine = $this->lines[--$this->currentLineNb]; - } - - /** - * Parses a YAML value. - * - * @param string $value A YAML value - * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise - * @param bool $objectSupport True if object support is enabled, false otherwise - * - * @return mixed A PHP value - * - * @throws ParseException When reference does not exist - */ - private function parseValue($value, $exceptionOnInvalidType, $objectSupport) - { - if (0 === strpos($value, '*')) { - if (false !== $pos = strpos($value, '#')) { - $value = substr($value, 1, $pos - 2); - } else { - $value = substr($value, 1); - } - - if (!array_key_exists($value, $this->refs)) { - throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLine); - } - - return $this->refs[$value]; - } - - if (preg_match('/^'.self::FOLDED_SCALAR_PATTERN.'$/', $value, $matches)) { - $modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : ''; - - return $this->parseFoldedScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), intval(abs($modifiers))); - } - - try { - return Inline::parse($value, $exceptionOnInvalidType, $objectSupport); - } catch (ParseException $e) { - $e->setParsedLine($this->getRealCurrentLineNb() + 1); - $e->setSnippet($this->currentLine); - - throw $e; - } - } - - /** - * Parses a folded scalar. - * - * @param string $separator The separator that was used to begin this folded scalar (| or >) - * @param string $indicator The indicator that was used to begin this folded scalar (+ or -) - * @param int $indentation The indentation that was used to begin this folded scalar - * - * @return string The text value - */ - private function parseFoldedScalar($separator, $indicator = '', $indentation = 0) - { - $notEOF = $this->moveToNextLine(); - if (!$notEOF) { - return ''; - } - - $isCurrentLineBlank = $this->isCurrentLineBlank(); - $text = ''; - - // leading blank lines are consumed before determining indentation - while ($notEOF && $isCurrentLineBlank) { - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $text .= "\n"; - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - - // determine indentation if not specified - if (0 === $indentation) { - if (preg_match('/^ +/', $this->currentLine, $matches)) { - $indentation = strlen($matches[0]); - } - } - - if ($indentation > 0) { - $pattern = sprintf('/^ {%d}(.*)$/', $indentation); - - while ( - $notEOF && ( - $isCurrentLineBlank || - preg_match($pattern, $this->currentLine, $matches) - ) - ) { - if ($isCurrentLineBlank) { - $text .= substr($this->currentLine, $indentation); - } else { - $text .= $matches[1]; - } - - // newline only if not EOF - if ($notEOF = $this->moveToNextLine()) { - $text .= "\n"; - $isCurrentLineBlank = $this->isCurrentLineBlank(); - } - } - } elseif ($notEOF) { - $text .= "\n"; - } - - if ($notEOF) { - $this->moveToPreviousLine(); - } - - // replace all non-trailing single newlines with spaces in folded blocks - if ('>' === $separator) { - preg_match('/(\n*)$/', $text, $matches); - $text = preg_replace('/(?getCurrentLineIndentation(); - $EOF = !$this->moveToNextLine(); - - while (!$EOF && $this->isCurrentLineEmpty()) { - $EOF = !$this->moveToNextLine(); - } - - if ($EOF) { - return false; - } - - $ret = false; - if ($this->getCurrentLineIndentation() > $currentIndentation) { - $ret = true; - } - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the current line is blank or if it is a comment line. - * - * @return bool Returns true if the current line is empty or if it is a comment line, false otherwise - */ - private function isCurrentLineEmpty() - { - return $this->isCurrentLineBlank() || $this->isCurrentLineComment(); - } - - /** - * Returns true if the current line is blank. - * - * @return bool Returns true if the current line is blank, false otherwise - */ - private function isCurrentLineBlank() - { - return '' == trim($this->currentLine, ' '); - } - - /** - * Returns true if the current line is a comment line. - * - * @return bool Returns true if the current line is a comment line, false otherwise - */ - private function isCurrentLineComment() - { - //checking explicitly the first char of the trim is faster than loops or strpos - $ltrimmedLine = ltrim($this->currentLine, ' '); - - return $ltrimmedLine[0] === '#'; - } - - /** - * Cleanups a YAML string to be parsed. - * - * @param string $value The input YAML string - * - * @return string A cleaned up YAML string - */ - private function cleanup($value) - { - $value = str_replace(array("\r\n", "\r"), "\n", $value); - - // strip YAML header - $count = 0; - $value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#su', '', $value, -1, $count); - $this->offset += $count; - - // remove leading comments - $trimmedValue = preg_replace('#^(\#.*?\n)+#s', '', $value, -1, $count); - if ($count == 1) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - } - - // remove start of the document marker (---) - $trimmedValue = preg_replace('#^\-\-\-.*?\n#s', '', $value, -1, $count); - if ($count == 1) { - // items have been removed, update the offset - $this->offset += substr_count($value, "\n") - substr_count($trimmedValue, "\n"); - $value = $trimmedValue; - - // remove end of the document marker (...) - $value = preg_replace('#\.\.\.\s*$#s', '', $value); - } - - return $value; - } - - /** - * Returns true if the next line starts unindented collection - * - * @return bool Returns true if the next line starts unindented collection, false otherwise - */ - private function isNextLineUnIndentedCollection() - { - $currentIndentation = $this->getCurrentLineIndentation(); - $notEOF = $this->moveToNextLine(); - - while ($notEOF && $this->isCurrentLineEmpty()) { - $notEOF = $this->moveToNextLine(); - } - - if (false === $notEOF) { - return false; - } - - $ret = false; - if ( - $this->getCurrentLineIndentation() == $currentIndentation - && - $this->isStringUnIndentedCollectionItem($this->currentLine) - ) { - $ret = true; - } - - $this->moveToPreviousLine(); - - return $ret; - } - - /** - * Returns true if the string is un-indented collection item - * - * @return bool Returns true if the string is un-indented collection item, false otherwise - */ - private function isStringUnIndentedCollectionItem() - { - return (0 === strpos($this->currentLine, '- ')); - } - -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md b/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md deleted file mode 100644 index 941a3460e..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/README.md +++ /dev/null @@ -1,19 +0,0 @@ -Yaml Component -============== - -YAML implements most of the YAML 1.2 specification. - - use Symfony\Component\Yaml\Yaml; - - $array = Yaml::parse($file); - - print Yaml::dump($array); - -Resources ---------- - -You can run the unit tests with the following command: - - $ cd path/to/Symfony/Component/Yaml/ - $ composer.phar install - $ phpunit diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php deleted file mode 100644 index ec2c65e3d..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/DumperTest.php +++ /dev/null @@ -1,207 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Yaml; -use Symfony\Component\Yaml\Parser; -use Symfony\Component\Yaml\Dumper; - -class DumperTest extends \PHPUnit_Framework_TestCase -{ - protected $parser; - protected $dumper; - protected $path; - - protected $array = array( - '' => 'bar', - 'foo' => '#bar', - 'foo\'bar' => array(), - 'bar' => array(1, 'foo'), - 'foobar' => array( - 'foo' => 'bar', - 'bar' => array(1, 'foo'), - 'foobar' => array( - 'foo' => 'bar', - 'bar' => array(1, 'foo'), - ), - ), - ); - - protected function setUp() - { - $this->parser = new Parser(); - $this->dumper = new Dumper(); - $this->path = __DIR__.'/Fixtures'; - } - - protected function tearDown() - { - $this->parser = null; - $this->dumper = null; - $this->path = null; - $this->array = null; - } - - public function testSetIndentation() - { - $this->dumper->setIndentation(7); - -$expected = <<assertEquals($expected, $this->dumper->dump($this->array, 4, 0)); - } - - public function testSpecifications() - { - $files = $this->parser->parse(file_get_contents($this->path.'/index.yml')); - foreach ($files as $file) { - $yamls = file_get_contents($this->path.'/'.$file.'.yml'); - - // split YAMLs documents - foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { - if (!$yaml) { - continue; - } - - $test = $this->parser->parse($yaml); - if (isset($test['dump_skip']) && $test['dump_skip']) { - continue; - } elseif (isset($test['todo']) && $test['todo']) { - // TODO - } else { - eval('$expected = '.trim($test['php']).';'); - - $this->assertEquals($expected, $this->parser->parse($this->dumper->dump($expected, 10)), $test['test']); - } - } - } - } - - public function testInlineLevel() - { - $expected = <<assertEquals($expected, $this->dumper->dump($this->array, -10), '->dump() takes an inline level argument'); -$this->assertEquals($expected, $this->dumper->dump($this->array, 0), '->dump() takes an inline level argument'); - -$expected = <<assertEquals($expected, $this->dumper->dump($this->array, 1), '->dump() takes an inline level argument'); - - $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 2), '->dump() takes an inline level argument'); - - $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 3), '->dump() takes an inline level argument'); - - $expected = <<assertEquals($expected, $this->dumper->dump($this->array, 4), '->dump() takes an inline level argument'); - $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument'); - } - - public function testObjectSupportEnabled() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, false, true); - - $this->assertEquals('{ foo: !!php/object:O:30:"Symfony\Component\Yaml\Tests\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects'); - } - - public function testObjectSupportDisabledButNoExceptions() - { - $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1)); - - $this->assertEquals('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\DumpException - */ - public function testObjectSupportDisabledWithExceptions() - { - $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, true, false); - } -} - -class A -{ - public $a = 'foo'; -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml deleted file mode 100644 index 5f9c94275..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsAnchorAlias.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- %YAML:1.0 -test: Simple Alias Example -brief: > - If you need to refer to the same item of data twice, - you can give that item an alias. The alias is a plain - string, starting with an ampersand. The item may then - be referred to by the alias throughout your document - by using an asterisk before the name of the alias. - This is called an anchor. -yaml: | - - &showell Steve - - Clark - - Brian - - Oren - - *showell -php: | - array('Steve', 'Clark', 'Brian', 'Oren', 'Steve') - ---- -test: Alias of a Mapping -brief: > - An alias can be used on any item of data, including - sequences, mappings, and other complex data types. -yaml: | - - &hello - Meat: pork - Starch: potato - - banana - - *hello -php: | - array(array('Meat'=>'pork', 'Starch'=>'potato'), 'banana', array('Meat'=>'pork', 'Starch'=>'potato')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml deleted file mode 100644 index ac0efa88d..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBasicTests.yml +++ /dev/null @@ -1,178 +0,0 @@ ---- %YAML:1.0 -test: Simple Sequence -brief: | - You can specify a list in YAML by placing each - member of the list on a new line with an opening - dash. These lists are called sequences. -yaml: | - - apple - - banana - - carrot -php: | - array('apple', 'banana', 'carrot') ---- -test: Nested Sequences -brief: | - You can include a sequence within another - sequence by giving the sequence an empty - dash, followed by an indented list. -yaml: | - - - - foo - - bar - - baz -php: | - array(array('foo', 'bar', 'baz')) ---- -test: Mixed Sequences -brief: | - Sequences can contain any YAML data, - including strings and other sequences. -yaml: | - - apple - - - - foo - - bar - - x123 - - banana - - carrot -php: | - array('apple', array('foo', 'bar', 'x123'), 'banana', 'carrot') ---- -test: Deeply Nested Sequences -brief: | - Sequences can be nested even deeper, with each - level of indentation representing a level of - depth. -yaml: | - - - - - - uno - - dos -php: | - array(array(array('uno', 'dos'))) ---- -test: Simple Mapping -brief: | - You can add a keyed list (also known as a dictionary or - hash) to your document by placing each member of the - list on a new line, with a colon separating the key - from its value. In YAML, this type of list is called - a mapping. -yaml: | - foo: whatever - bar: stuff -php: | - array('foo' => 'whatever', 'bar' => 'stuff') ---- -test: Sequence in a Mapping -brief: | - A value in a mapping can be a sequence. -yaml: | - foo: whatever - bar: - - uno - - dos -php: | - array('foo' => 'whatever', 'bar' => array('uno', 'dos')) ---- -test: Nested Mappings -brief: | - A value in a mapping can be another mapping. -yaml: | - foo: whatever - bar: - fruit: apple - name: steve - sport: baseball -php: | - array( - 'foo' => 'whatever', - 'bar' => array( - 'fruit' => 'apple', - 'name' => 'steve', - 'sport' => 'baseball' - ) - ) ---- -test: Mixed Mapping -brief: | - A mapping can contain any assortment - of mappings and sequences as values. -yaml: | - foo: whatever - bar: - - - fruit: apple - name: steve - sport: baseball - - more - - - python: rocks - perl: papers - ruby: scissorses -php: | - array( - 'foo' => 'whatever', - 'bar' => array( - array( - 'fruit' => 'apple', - 'name' => 'steve', - 'sport' => 'baseball' - ), - 'more', - array( - 'python' => 'rocks', - 'perl' => 'papers', - 'ruby' => 'scissorses' - ) - ) - ) ---- -test: Mapping-in-Sequence Shortcut -todo: true -brief: | - If you are adding a mapping to a sequence, you - can place the mapping on the same line as the - dash as a shortcut. -yaml: | - - work on YAML.py: - - work on Store -php: | - array(array('work on YAML.py' => array('work on Store'))) ---- -test: Sequence-in-Mapping Shortcut -todo: true -brief: | - The dash in a sequence counts as indentation, so - you can add a sequence inside of a mapping without - needing spaces as indentation. -yaml: | - allow: - - 'localhost' - - '%.sourceforge.net' - - '%.freepan.org' -php: | - array('allow' => array('localhost', '%.sourceforge.net', '%.freepan.org')) ---- -todo: true -test: Merge key -brief: | - A merge key ('<<') can be used in a mapping to insert other mappings. If - the value associated with the merge key is a mapping, each of its key/value - pairs is inserted into the current mapping. -yaml: | - mapping: - name: Joe - job: Accountant - <<: - age: 38 -php: | - array( - 'mapping' => - array( - 'name' => 'Joe', - 'job' => 'Accountant', - 'age' => 38 - ) - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml deleted file mode 100644 index f7ca469b4..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsBlockMapping.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -test: One Element Mapping -brief: | - A mapping with one key/value pair -yaml: | - foo: bar -php: | - array('foo' => 'bar') ---- -test: Multi Element Mapping -brief: | - More than one key/value pair -yaml: | - red: baron - white: walls - blue: berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) ---- -test: Values aligned -brief: | - Often times human editors of documents will align the values even - though YAML emitters generally don't. -yaml: | - red: baron - white: walls - blue: berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) ---- -test: Colons aligned -brief: | - Spaces can come before the ': ' key/value separator. -yaml: | - red : baron - white : walls - blue : berries -php: | - array( - 'red' => 'baron', - 'white' => 'walls', - 'blue' => 'berries', - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml deleted file mode 100644 index f8501ddc2..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsDocumentSeparator.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- %YAML:1.0 -test: Trailing Document Separator -todo: true -brief: > - You can separate YAML documents - with a string of three dashes. -yaml: | - - foo: 1 - bar: 2 - --- - more: stuff -python: | - [ - [ { 'foo': 1, 'bar': 2 } ], - { 'more': 'stuff' } - ] -ruby: | - [ { 'foo' => 1, 'bar' => 2 } ] - ---- -test: Leading Document Separator -todo: true -brief: > - You can explicity give an opening - document separator to your YAML stream. -yaml: | - --- - - foo: 1 - bar: 2 - --- - more: stuff -python: | - [ - [ {'foo': 1, 'bar': 2}], - {'more': 'stuff'} - ] -ruby: | - [ { 'foo' => 1, 'bar' => 2 } ] - ---- -test: YAML Header -todo: true -brief: > - The opening separator can contain directives - to the YAML parser, such as the version - number. -yaml: | - --- %YAML:1.0 - foo: 1 - bar: 2 -php: | - array('foo' => 1, 'bar' => 2) -documents: 1 - ---- -test: Red Herring Document Separator -brief: > - Separators included in blocks or strings - are treated as blocks or strings, as the - document separator should have no indentation - preceding it. -yaml: | - foo: | - --- -php: | - array('foo' => "---\n") - ---- -test: Multiple Document Separators in Block -brief: > - This technique allows you to embed other YAML - documents within literal blocks. -yaml: | - foo: | - --- - foo: bar - --- - yo: baz - bar: | - fooness -php: | - array( - 'foo' => "---\nfoo: bar\n---\nyo: baz\n", - 'bar' => "fooness\n" - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml deleted file mode 100644 index e8506fcb6..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsErrorTests.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -test: Missing value for hash item -todo: true -brief: | - Third item in this hash doesn't have a value -yaml: | - okay: value - also okay: ~ - causes error because no value specified - last key: value okay here too -python-error: causes error because no value specified - ---- -test: Not indenting enough -brief: | - There was a bug in PyYaml where it was off by one - in the indentation check. It was allowing the YAML - below. -# This is actually valid YAML now. Someone should tell showell. -yaml: | - foo: - firstline: 1 - secondline: 2 -php: | - array('foo' => null, 'firstline' => 1, 'secondline' => 2) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml deleted file mode 100644 index 03090e4ab..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFlowCollections.yml +++ /dev/null @@ -1,60 +0,0 @@ ---- -test: Simple Inline Array -brief: > - Sequences can be contained on a - single line, using the inline syntax. - Separate each entry with commas and - enclose in square brackets. -yaml: | - seq: [ a, b, c ] -php: | - array('seq' => array('a', 'b', 'c')) ---- -test: Simple Inline Hash -brief: > - Mapping can also be contained on - a single line, using the inline - syntax. Each key-value pair is - separated by a colon, with a comma - between each entry in the mapping. - Enclose with curly braces. -yaml: | - hash: { name: Steve, foo: bar } -php: | - array('hash' => array('name' => 'Steve', 'foo' => 'bar')) ---- -test: Multi-line Inline Collections -todo: true -brief: > - Both inline sequences and inline mappings - can span multiple lines, provided that you - indent the additional lines. -yaml: | - languages: [ Ruby, - Perl, - Python ] - websites: { YAML: yaml.org, - Ruby: ruby-lang.org, - Python: python.org, - Perl: use.perl.org } -php: | - array( - 'languages' => array('Ruby', 'Perl', 'Python'), - 'websites' => array( - 'YAML' => 'yaml.org', - 'Ruby' => 'ruby-lang.org', - 'Python' => 'python.org', - 'Perl' => 'use.perl.org' - ) - ) ---- -test: Commas in Values (not in the spec!) -todo: true -brief: > - List items in collections are delimited by commas, but - there must be a space after each comma. This allows you - to add numbers without quoting. -yaml: | - attendances: [ 45,123, 70,000, 17,222 ] -php: | - array('attendances' => array(45123, 70000, 17222)) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml deleted file mode 100644 index a14735a55..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsFoldedScalars.yml +++ /dev/null @@ -1,176 +0,0 @@ ---- %YAML:1.0 -test: Single ending newline -brief: > - A pipe character, followed by an indented - block of text is treated as a literal - block, in which newlines are preserved - throughout the block, including the final - newline. -yaml: | - --- - this: | - Foo - Bar -php: | - array('this' => "Foo\nBar\n") ---- -test: The '+' indicator -brief: > - The '+' indicator says to keep newlines at the end of text - blocks. -yaml: | - normal: | - extra new lines not kept - - preserving: |+ - extra new lines are kept - - - dummy: value -php: | - array( - 'normal' => "extra new lines not kept\n", - 'preserving' => "extra new lines are kept\n\n\n", - 'dummy' => 'value' - ) ---- -test: Three trailing newlines in literals -brief: > - To give you more control over how space - is preserved in text blocks, YAML has - the keep '+' and chomp '-' indicators. - The keep indicator will preserve all - ending newlines, while the chomp indicator - will strip all ending newlines. -yaml: | - clipped: | - This has one newline. - - - - same as "clipped" above: "This has one newline.\n" - - stripped: |- - This has no newline. - - - - same as "stripped" above: "This has no newline." - - kept: |+ - This has four newlines. - - - - same as "kept" above: "This has four newlines.\n\n\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has four newlines.\n\n\n\n", - 'same as "kept" above' => "This has four newlines.\n\n\n\n" - ) ---- -test: Extra trailing newlines with spaces -todo: true -brief: > - Normally, only a single newline is kept - from the end of a literal block, unless the - keep '+' character is used in combination - with the pipe. The following example - will preserve all ending whitespace - since the last line of both literal blocks - contains spaces which extend past the indentation - level. -yaml: | - --- - this: | - Foo - - - kept: |+ - Foo - - -php: | - array('this' => "Foo\n\n \n", - 'kept' => "Foo\n\n \n" ) - ---- -test: Folded Block in a Sequence -brief: > - A greater-then character, followed by an indented - block of text is treated as a folded block, in - which lines of text separated by a single newline - are concatenated as a single line. -yaml: | - --- - - apple - - banana - - > - can't you see - the beauty of yaml? - hmm - - dog -php: | - array( - 'apple', - 'banana', - "can't you see the beauty of yaml? hmm\n", - 'dog' - ) ---- -test: Folded Block as a Mapping Value -brief: > - Both literal and folded blocks can be - used in collections, as values in a - sequence or a mapping. -yaml: | - --- - quote: > - Mark McGwire's - year was crippled - by a knee injury. - source: espn -php: | - array( - 'quote' => "Mark McGwire's year was crippled by a knee injury.\n", - 'source' => 'espn' - ) ---- -test: Three trailing newlines in folded blocks -brief: > - The keep and chomp indicators can also - be applied to folded blocks. -yaml: | - clipped: > - This has one newline. - - - - same as "clipped" above: "This has one newline.\n" - - stripped: >- - This has no newline. - - - - same as "stripped" above: "This has no newline." - - kept: >+ - This has four newlines. - - - - same as "kept" above: "This has four newlines.\n\n\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has four newlines.\n\n\n\n", - 'same as "kept" above' => "This has four newlines.\n\n\n\n" - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml deleted file mode 100644 index 9a5300f2e..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsNullsAndEmpties.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- %YAML:1.0 -test: Empty Sequence -brief: > - You can represent the empty sequence - with an empty inline sequence. -yaml: | - empty: [] -php: | - array('empty' => array()) ---- -test: Empty Mapping -brief: > - You can represent the empty mapping - with an empty inline mapping. -yaml: | - empty: {} -php: | - array('empty' => array()) ---- -test: Empty Sequence as Entire Document -yaml: | - [] -php: | - array() ---- -test: Empty Mapping as Entire Document -yaml: | - {} -php: | - array() ---- -test: Null as Document -yaml: | - ~ -php: | - null ---- -test: Empty String -brief: > - You can represent an empty string - with a pair of quotes. -yaml: | - '' -php: | - '' diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml deleted file mode 100644 index 1e59f3bf9..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml +++ /dev/null @@ -1,1695 +0,0 @@ ---- %YAML:1.0 -test: Sequence of scalars -spec: 2.1 -yaml: | - - Mark McGwire - - Sammy Sosa - - Ken Griffey -php: | - array('Mark McGwire', 'Sammy Sosa', 'Ken Griffey') ---- -test: Mapping of scalars to scalars -spec: 2.2 -yaml: | - hr: 65 - avg: 0.278 - rbi: 147 -php: | - array('hr' => 65, 'avg' => 0.278, 'rbi' => 147) ---- -test: Mapping of scalars to sequences -spec: 2.3 -yaml: | - american: - - Boston Red Sox - - Detroit Tigers - - New York Yankees - national: - - New York Mets - - Chicago Cubs - - Atlanta Braves -php: | - array('american' => - array( 'Boston Red Sox', 'Detroit Tigers', - 'New York Yankees' ), - 'national' => - array( 'New York Mets', 'Chicago Cubs', - 'Atlanta Braves' ) - ) ---- -test: Sequence of mappings -spec: 2.4 -yaml: | - - - name: Mark McGwire - hr: 65 - avg: 0.278 - - - name: Sammy Sosa - hr: 63 - avg: 0.288 -php: | - array( - array('name' => 'Mark McGwire', 'hr' => 65, 'avg' => 0.278), - array('name' => 'Sammy Sosa', 'hr' => 63, 'avg' => 0.288) - ) ---- -test: Legacy A5 -todo: true -spec: legacy_A5 -yaml: | - ? - - New York Yankees - - Atlanta Braves - : - - 2001-07-02 - - 2001-08-12 - - 2001-08-14 - ? - - Detroit Tigers - - Chicago Cubs - : - - 2001-07-23 -perl-busted: > - YAML.pm will be able to emulate this behavior soon. In this regard - it may be somewhat more correct than Python's native behaviour which - can only use tuples as mapping keys. PyYAML will also need to figure - out some clever way to roundtrip structured keys. -python: | - [ - { - ('New York Yankees', 'Atlanta Braves'): - [yaml.timestamp('2001-07-02'), - yaml.timestamp('2001-08-12'), - yaml.timestamp('2001-08-14')], - ('Detroit Tigers', 'Chicago Cubs'): - [yaml.timestamp('2001-07-23')] - } - ] -ruby: | - { - [ 'New York Yankees', 'Atlanta Braves' ] => - [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ], - [ 'Detroit Tigers', 'Chicago Cubs' ] => - [ Date.new( 2001, 7, 23 ) ] - } -syck: | - struct test_node seq1[] = { - { T_STR, 0, "New York Yankees" }, - { T_STR, 0, "Atlanta Braves" }, - end_node - }; - struct test_node seq2[] = { - { T_STR, 0, "2001-07-02" }, - { T_STR, 0, "2001-08-12" }, - { T_STR, 0, "2001-08-14" }, - end_node - }; - struct test_node seq3[] = { - { T_STR, 0, "Detroit Tigers" }, - { T_STR, 0, "Chicago Cubs" }, - end_node - }; - struct test_node seq4[] = { - { T_STR, 0, "2001-07-23" }, - end_node - }; - struct test_node map[] = { - { T_SEQ, 0, 0, seq1 }, - { T_SEQ, 0, 0, seq2 }, - { T_SEQ, 0, 0, seq3 }, - { T_SEQ, 0, 0, seq4 }, - end_node - }; - struct test_node stream[] = { - { T_MAP, 0, 0, map }, - end_node - }; - ---- -test: Sequence of sequences -spec: 2.5 -yaml: | - - [ name , hr , avg ] - - [ Mark McGwire , 65 , 0.278 ] - - [ Sammy Sosa , 63 , 0.288 ] -php: | - array( - array( 'name', 'hr', 'avg' ), - array( 'Mark McGwire', 65, 0.278 ), - array( 'Sammy Sosa', 63, 0.288 ) - ) ---- -test: Mapping of mappings -todo: true -spec: 2.6 -yaml: | - Mark McGwire: {hr: 65, avg: 0.278} - Sammy Sosa: { - hr: 63, - avg: 0.288 - } -php: | - array( - 'Mark McGwire' => - array( 'hr' => 65, 'avg' => 0.278 ), - 'Sammy Sosa' => - array( 'hr' => 63, 'avg' => 0.288 ) - ) ---- -test: Two documents in a stream each with a leading comment -todo: true -spec: 2.7 -yaml: | - # Ranking of 1998 home runs - --- - - Mark McGwire - - Sammy Sosa - - Ken Griffey - - # Team ranking - --- - - Chicago Cubs - - St Louis Cardinals -ruby: | - y = YAML::Stream.new - y.add( [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] ) - y.add( [ 'Chicago Cubs', 'St Louis Cardinals' ] ) -documents: 2 - ---- -test: Play by play feed from a game -todo: true -spec: 2.8 -yaml: | - --- - time: 20:03:20 - player: Sammy Sosa - action: strike (miss) - ... - --- - time: 20:03:47 - player: Sammy Sosa - action: grand slam - ... -perl: | - [ 'Mark McGwire', 'Sammy Sosa', 'Ken Griffey' ] -documents: 2 - ---- -test: Single document with two comments -spec: 2.9 -yaml: | - hr: # 1998 hr ranking - - Mark McGwire - - Sammy Sosa - rbi: - # 1998 rbi ranking - - Sammy Sosa - - Ken Griffey -php: | - array( - 'hr' => array( 'Mark McGwire', 'Sammy Sosa' ), - 'rbi' => array( 'Sammy Sosa', 'Ken Griffey' ) - ) ---- -test: Node for Sammy Sosa appears twice in this document -spec: 2.10 -yaml: | - --- - hr: - - Mark McGwire - # Following node labeled SS - - &SS Sammy Sosa - rbi: - - *SS # Subsequent occurrence - - Ken Griffey -php: | - array( - 'hr' => - array('Mark McGwire', 'Sammy Sosa'), - 'rbi' => - array('Sammy Sosa', 'Ken Griffey') - ) ---- -test: Mapping between sequences -todo: true -spec: 2.11 -yaml: | - ? # PLAY SCHEDULE - - Detroit Tigers - - Chicago Cubs - : - - 2001-07-23 - - ? [ New York Yankees, - Atlanta Braves ] - : [ 2001-07-02, 2001-08-12, - 2001-08-14 ] -ruby: | - { - [ 'Detroit Tigers', 'Chicago Cubs' ] => [ Date.new( 2001, 7, 23 ) ], - [ 'New York Yankees', 'Atlanta Braves' ] => [ Date.new( 2001, 7, 2 ), Date.new( 2001, 8, 12 ), Date.new( 2001, 8, 14 ) ] - } -syck: | - struct test_node seq1[] = { - { T_STR, 0, "New York Yankees" }, - { T_STR, 0, "Atlanta Braves" }, - end_node - }; - struct test_node seq2[] = { - { T_STR, 0, "2001-07-02" }, - { T_STR, 0, "2001-08-12" }, - { T_STR, 0, "2001-08-14" }, - end_node - }; - struct test_node seq3[] = { - { T_STR, 0, "Detroit Tigers" }, - { T_STR, 0, "Chicago Cubs" }, - end_node - }; - struct test_node seq4[] = { - { T_STR, 0, "2001-07-23" }, - end_node - }; - struct test_node map[] = { - { T_SEQ, 0, 0, seq3 }, - { T_SEQ, 0, 0, seq4 }, - { T_SEQ, 0, 0, seq1 }, - { T_SEQ, 0, 0, seq2 }, - end_node - }; - struct test_node stream[] = { - { T_MAP, 0, 0, map }, - end_node - }; - ---- -test: Sequence key shortcut -spec: 2.12 -yaml: | - --- - # products purchased - - item : Super Hoop - quantity: 1 - - item : Basketball - quantity: 4 - - item : Big Shoes - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'item' => 'Basketball', - 'quantity' => 4, - ), - array ( - 'item' => 'Big Shoes', - 'quantity' => 1, - ) - ) -perl: | - [ - { item => 'Super Hoop', quantity => 1 }, - { item => 'Basketball', quantity => 4 }, - { item => 'Big Shoes', quantity => 1 } - ] - -ruby: | - [ - { 'item' => 'Super Hoop', 'quantity' => 1 }, - { 'item' => 'Basketball', 'quantity' => 4 }, - { 'item' => 'Big Shoes', 'quantity' => 1 } - ] -python: | - [ - { 'item': 'Super Hoop', 'quantity': 1 }, - { 'item': 'Basketball', 'quantity': 4 }, - { 'item': 'Big Shoes', 'quantity': 1 } - ] -syck: | - struct test_node map1[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Super Hoop" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "1" }, - end_node - }; - struct test_node map2[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Basketball" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "4" }, - end_node - }; - struct test_node map3[] = { - { T_STR, 0, "item" }, - { T_STR, 0, "Big Shoes" }, - { T_STR, 0, "quantity" }, - { T_STR, 0, "1" }, - end_node - }; - struct test_node seq[] = { - { T_MAP, 0, 0, map1 }, - { T_MAP, 0, 0, map2 }, - { T_MAP, 0, 0, map3 }, - end_node - }; - struct test_node stream[] = { - { T_SEQ, 0, 0, seq }, - end_node - }; - - ---- -test: Literal perserves newlines -todo: true -spec: 2.13 -yaml: | - # ASCII Art - --- | - \//||\/|| - // || ||_ -perl: | - "\\//||\\/||\n// || ||_\n" -ruby: | - "\\//||\\/||\n// || ||_\n" -python: | - [ - flushLeft( - """ - \//||\/|| - // || ||_ - """ - ) - ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "\\//||\\/||\n// || ||_\n" }, - end_node - }; - ---- -test: Folded treats newlines as a space -todo: true -spec: 2.14 -yaml: | - --- - Mark McGwire's - year was crippled - by a knee injury. -perl: | - "Mark McGwire's year was crippled by a knee injury." -ruby: | - "Mark McGwire's year was crippled by a knee injury." -python: | - [ "Mark McGwire's year was crippled by a knee injury." ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "Mark McGwire's year was crippled by a knee injury." }, - end_node - }; - ---- -test: Newlines preserved for indented and blank lines -todo: true -spec: 2.15 -yaml: | - --- > - Sammy Sosa completed another - fine season with great stats. - - 63 Home Runs - 0.288 Batting Average - - What a year! -perl: | - "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" -ruby: | - "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" -python: | - [ - flushLeft( - """ - Sammy Sosa completed another fine season with great stats. - - 63 Home Runs - 0.288 Batting Average - - What a year! - """ - ) - ] -syck: | - struct test_node stream[] = { - { T_STR, 0, "Sammy Sosa completed another fine season with great stats.\n\n 63 Home Runs\n 0.288 Batting Average\n\nWhat a year!\n" }, - end_node - }; - - ---- -test: Indentation determines scope -spec: 2.16 -yaml: | - name: Mark McGwire - accomplishment: > - Mark set a major league - home run record in 1998. - stats: | - 65 Home Runs - 0.278 Batting Average -php: | - array( - 'name' => 'Mark McGwire', - 'accomplishment' => "Mark set a major league home run record in 1998.\n", - 'stats' => "65 Home Runs\n0.278 Batting Average\n" - ) ---- -test: Quoted scalars -todo: true -spec: 2.17 -yaml: | - unicode: "Sosa did fine.\u263A" - control: "\b1998\t1999\t2000\n" - hexesc: "\x0D\x0A is \r\n" - - single: '"Howdy!" he cried.' - quoted: ' # not a ''comment''.' - tie-fighter: '|\-*-/|' -ruby: | - { - "tie-fighter" => "|\\-*-/|", - "control"=>"\0101998\t1999\t2000\n", - "unicode"=>"Sosa did fine." + ["263A".hex ].pack('U*'), - "quoted"=>" # not a 'comment'.", - "single"=>"\"Howdy!\" he cried.", - "hexesc"=>"\r\n is \r\n" - } ---- -test: Multiline flow scalars -todo: true -spec: 2.18 -yaml: | - plain: - This unquoted scalar - spans many lines. - - quoted: "So does this - quoted scalar.\n" -ruby: | - { - 'plain' => 'This unquoted scalar spans many lines.', - 'quoted' => "So does this quoted scalar.\n" - } ---- -test: Integers -spec: 2.19 -yaml: | - canonical: 12345 - decimal: +12,345 - octal: 014 - hexadecimal: 0xC -php: | - array( - 'canonical' => 12345, - 'decimal' => 12345, - 'octal' => 014, - 'hexadecimal' => 0xC - ) ---- -# FIX: spec shows parens around -inf and NaN -test: Floating point -spec: 2.20 -yaml: | - canonical: 1.23015e+3 - exponential: 12.3015e+02 - fixed: 1,230.15 - negative infinity: -.inf - not a number: .NaN -php: | - array( - 'canonical' => 1230.15, - 'exponential' => 1230.15, - 'fixed' => 1230.15, - 'negative infinity' => log(0), - 'not a number' => -log(0), - ) ---- -test: Miscellaneous -spec: 2.21 -yaml: | - null: ~ - true: true - false: false - string: '12345' -php: | - array( - '' => null, - 1 => true, - 0 => false, - 'string' => '12345' - ) ---- -test: Timestamps -todo: true -spec: 2.22 -yaml: | - canonical: 2001-12-15T02:59:43.1Z - iso8601: 2001-12-14t21:59:43.10-05:00 - spaced: 2001-12-14 21:59:43.10 -05:00 - date: 2002-12-14 # Time is noon UTC -php: | - array( - 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), - 'iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'date' => Date.new( 2002, 12, 14 ) - ) ---- -test: legacy Timestamps test -todo: true -spec: legacy D4 -yaml: | - canonical: 2001-12-15T02:59:43.00Z - iso8601: 2001-02-28t21:59:43.00-05:00 - spaced: 2001-12-14 21:59:43.00 -05:00 - date: 2002-12-14 -php: | - array( - 'canonical' => Time::utc( 2001, 12, 15, 2, 59, 43, 0 ), - 'iso8601' => YAML::mktime( 2001, 2, 28, 21, 59, 43, 0, "-05:00" ), - 'spaced' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0, "-05:00" ), - 'date' => Date.new( 2002, 12, 14 ) - ) ---- -test: Various explicit families -todo: true -spec: 2.23 -yaml: | - not-date: !str 2002-04-28 - picture: !binary | - R0lGODlhDAAMAIQAAP//9/X - 17unp5WZmZgAAAOfn515eXv - Pz7Y6OjuDg4J+fn5OTk6enp - 56enmleECcgggoBADs= - - application specific tag: !!something | - The semantics of the tag - above may be different for - different documents. - -ruby-setup: | - YAML.add_private_type( "something" ) do |type, val| - "SOMETHING: #{val}" - end -ruby: | - { - 'not-date' => '2002-04-28', - 'picture' => "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236i^\020' \202\n\001\000;", - 'application specific tag' => "SOMETHING: The semantics of the tag\nabove may be different for\ndifferent documents.\n" - } ---- -test: Application specific family -todo: true -spec: 2.24 -yaml: | - # Establish a tag prefix - --- !clarkevans.com,2002/graph/^shape - # Use the prefix: shorthand for - # !clarkevans.com,2002/graph/circle - - !^circle - center: &ORIGIN {x: 73, 'y': 129} - radius: 7 - - !^line # !clarkevans.com,2002/graph/line - start: *ORIGIN - finish: { x: 89, 'y': 102 } - - !^label - start: *ORIGIN - color: 0xFFEEBB - value: Pretty vector drawing. -ruby-setup: | - YAML.add_domain_type( "clarkevans.com,2002", 'graph/shape' ) { |type, val| - if Array === val - val << "Shape Container" - val - else - raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect - end - } - one_shape_proc = Proc.new { |type, val| - scheme, domain, type = type.split( /:/, 3 ) - if val.is_a? ::Hash - val['TYPE'] = "Shape: #{type}" - val - else - raise YAML::Error, "Invalid graph of class #{ val.class }: " + val.inspect - end - } - YAML.add_domain_type( "clarkevans.com,2002", 'graph/circle', &one_shape_proc ) - YAML.add_domain_type( "clarkevans.com,2002", 'graph/line', &one_shape_proc ) - YAML.add_domain_type( "clarkevans.com,2002", 'graph/label', &one_shape_proc ) -ruby: | - [ - { - "radius" => 7, - "center"=> - { - "x" => 73, - "y" => 129 - }, - "TYPE" => "Shape: graph/circle" - }, { - "finish" => - { - "x" => 89, - "y" => 102 - }, - "TYPE" => "Shape: graph/line", - "start" => - { - "x" => 73, - "y" => 129 - } - }, { - "TYPE" => "Shape: graph/label", - "value" => "Pretty vector drawing.", - "start" => - { - "x" => 73, - "y" => 129 - }, - "color" => 16772795 - }, - "Shape Container" - ] -# --- -# test: Unordered set -# spec: 2.25 -# yaml: | -# # sets are represented as a -# # mapping where each key is -# # associated with the empty string -# --- !set -# ? Mark McGwire -# ? Sammy Sosa -# ? Ken Griff ---- -test: Ordered mappings -todo: true -spec: 2.26 -yaml: | - # ordered maps are represented as - # a sequence of mappings, with - # each mapping having one key - --- !omap - - Mark McGwire: 65 - - Sammy Sosa: 63 - - Ken Griffy: 58 -ruby: | - YAML::Omap[ - 'Mark McGwire', 65, - 'Sammy Sosa', 63, - 'Ken Griffy', 58 - ] ---- -test: Invoice -dump_skip: true -spec: 2.27 -yaml: | - --- !clarkevans.com,2002/^invoice - invoice: 34843 - date : 2001-01-23 - bill-to: &id001 - given : Chris - family : Dumars - address: - lines: | - 458 Walkman Dr. - Suite #292 - city : Royal Oak - state : MI - postal : 48046 - ship-to: *id001 - product: - - - sku : BL394D - quantity : 4 - description : Basketball - price : 450.00 - - - sku : BL4438H - quantity : 1 - description : Super Hoop - price : 2392.00 - tax : 251.42 - total: 4443.52 - comments: > - Late afternoon is best. - Backup contact is Nancy - Billsmer @ 338-4338. -php: | - array( - 'invoice' => 34843, 'date' => mktime(0, 0, 0, 1, 23, 2001), - 'bill-to' => - array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) - , 'ship-to' => - array( 'given' => 'Chris', 'family' => 'Dumars', 'address' => array( 'lines' => "458 Walkman Dr.\nSuite #292\n", 'city' => 'Royal Oak', 'state' => 'MI', 'postal' => 48046 ) ) - , 'product' => - array( - array( 'sku' => 'BL394D', 'quantity' => 4, 'description' => 'Basketball', 'price' => 450.00 ), - array( 'sku' => 'BL4438H', 'quantity' => 1, 'description' => 'Super Hoop', 'price' => 2392.00 ) - ), - 'tax' => 251.42, 'total' => 4443.52, - 'comments' => "Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.\n" - ) ---- -test: Log file -todo: true -spec: 2.28 -yaml: | - --- - Time: 2001-11-23 15:01:42 -05:00 - User: ed - Warning: > - This is an error message - for the log file - --- - Time: 2001-11-23 15:02:31 -05:00 - User: ed - Warning: > - A slightly different error - message. - --- - Date: 2001-11-23 15:03:17 -05:00 - User: ed - Fatal: > - Unknown variable "bar" - Stack: - - file: TopClass.py - line: 23 - code: | - x = MoreObject("345\n") - - file: MoreClass.py - line: 58 - code: |- - foo = bar -ruby: | - y = YAML::Stream.new - y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 01, 42, 00, "-05:00" ), - 'User' => 'ed', 'Warning' => "This is an error message for the log file\n" } ) - y.add( { 'Time' => YAML::mktime( 2001, 11, 23, 15, 02, 31, 00, "-05:00" ), - 'User' => 'ed', 'Warning' => "A slightly different error message.\n" } ) - y.add( { 'Date' => YAML::mktime( 2001, 11, 23, 15, 03, 17, 00, "-05:00" ), - 'User' => 'ed', 'Fatal' => "Unknown variable \"bar\"\n", - 'Stack' => [ - { 'file' => 'TopClass.py', 'line' => 23, 'code' => "x = MoreObject(\"345\\n\")\n" }, - { 'file' => 'MoreClass.py', 'line' => 58, 'code' => "foo = bar" } ] } ) -documents: 3 - ---- -test: Throwaway comments -yaml: | - ### These are four throwaway comment ### - - ### lines (the second line is empty). ### - this: | # Comments may trail lines. - contains three lines of text. - The third one starts with a - # character. This isn't a comment. - - # These are three throwaway comment - # lines (the first line is empty). -php: | - array( - 'this' => "contains three lines of text.\nThe third one starts with a\n# character. This isn't a comment.\n" - ) ---- -test: Document with a single value -todo: true -yaml: | - --- > - This YAML stream contains a single text value. - The next stream is a log file - a sequence of - log entries. Adding an entry to the log is a - simple matter of appending it at the end. -ruby: | - "This YAML stream contains a single text value. The next stream is a log file - a sequence of log entries. Adding an entry to the log is a simple matter of appending it at the end.\n" ---- -test: Document stream -todo: true -yaml: | - --- - at: 2001-08-12 09:25:00.00 Z - type: GET - HTTP: '1.0' - url: '/index.html' - --- - at: 2001-08-12 09:25:10.00 Z - type: GET - HTTP: '1.0' - url: '/toc.html' -ruby: | - y = YAML::Stream.new - y.add( { - 'at' => Time::utc( 2001, 8, 12, 9, 25, 00 ), - 'type' => 'GET', - 'HTTP' => '1.0', - 'url' => '/index.html' - } ) - y.add( { - 'at' => Time::utc( 2001, 8, 12, 9, 25, 10 ), - 'type' => 'GET', - 'HTTP' => '1.0', - 'url' => '/toc.html' - } ) -documents: 2 - ---- -test: Top level mapping -yaml: | - # This stream is an example of a top-level mapping. - invoice : 34843 - date : 2001-01-23 - total : 4443.52 -php: | - array( - 'invoice' => 34843, - 'date' => mktime(0, 0, 0, 1, 23, 2001), - 'total' => 4443.52 - ) ---- -test: Single-line documents -todo: true -yaml: | - # The following is a sequence of three documents. - # The first contains an empty mapping, the second - # an empty sequence, and the last an empty string. - --- {} - --- [ ] - --- '' -ruby: | - y = YAML::Stream.new - y.add( {} ) - y.add( [] ) - y.add( '' ) -documents: 3 - ---- -test: Document with pause -todo: true -yaml: | - # A communication channel based on a YAML stream. - --- - sent at: 2002-06-06 11:46:25.10 Z - payload: Whatever - # Receiver can process this as soon as the following is sent: - ... - # Even if the next message is sent long after: - --- - sent at: 2002-06-06 12:05:53.47 Z - payload: Whatever - ... -ruby: | - y = YAML::Stream.new - y.add( - { 'sent at' => YAML::mktime( 2002, 6, 6, 11, 46, 25, 0.10 ), - 'payload' => 'Whatever' } - ) - y.add( - { "payload" => "Whatever", "sent at" => YAML::mktime( 2002, 6, 6, 12, 5, 53, 0.47 ) } - ) -documents: 2 - ---- -test: Explicit typing -yaml: | - integer: 12 - also int: ! "12" - string: !str 12 -php: | - array( 'integer' => 12, 'also int' => 12, 'string' => '12' ) ---- -test: Private types -todo: true -yaml: | - # Both examples below make use of the 'x-private:ball' - # type family URI, but with different semantics. - --- - pool: !!ball - number: 8 - color: black - --- - bearing: !!ball - material: steel -ruby: | - y = YAML::Stream.new - y.add( { 'pool' => - YAML::PrivateType.new( 'ball', - { 'number' => 8, 'color' => 'black' } ) } - ) - y.add( { 'bearing' => - YAML::PrivateType.new( 'ball', - { 'material' => 'steel' } ) } - ) -documents: 2 - ---- -test: Type family under yaml.org -yaml: | - # The URI is 'tag:yaml.org,2002:str' - - !str a Unicode string -php: | - array( 'a Unicode string' ) ---- -test: Type family under perl.yaml.org -todo: true -yaml: | - # The URI is 'tag:perl.yaml.org,2002:Text::Tabs' - - !perl/Text::Tabs {} -ruby: | - [ YAML::DomainType.new( 'perl.yaml.org,2002', 'Text::Tabs', {} ) ] ---- -test: Type family under clarkevans.com -todo: true -yaml: | - # The URI is 'tag:clarkevans.com,2003-02:timesheet' - - !clarkevans.com,2003-02/timesheet {} -ruby: | - [ YAML::DomainType.new( 'clarkevans.com,2003-02', 'timesheet', {} ) ] ---- -test: URI Escaping -todo: true -yaml: | - same: - - !domain.tld,2002/type\x30 value - - !domain.tld,2002/type0 value - different: # As far as the YAML parser is concerned - - !domain.tld,2002/type%30 value - - !domain.tld,2002/type0 value -ruby-setup: | - YAML.add_domain_type( "domain.tld,2002", "type0" ) { |type, val| - "ONE: #{val}" - } - YAML.add_domain_type( "domain.tld,2002", "type%30" ) { |type, val| - "TWO: #{val}" - } -ruby: | - { 'same' => [ 'ONE: value', 'ONE: value' ], 'different' => [ 'TWO: value', 'ONE: value' ] } ---- -test: URI Prefixing -todo: true -yaml: | - # 'tag:domain.tld,2002:invoice' is some type family. - invoice: !domain.tld,2002/^invoice - # 'seq' is shorthand for 'tag:yaml.org,2002:seq'. - # This does not effect '^customer' below - # because it is does not specify a prefix. - customers: !seq - # '^customer' is shorthand for the full - # notation 'tag:domain.tld,2002:customer'. - - !^customer - given : Chris - family : Dumars -ruby-setup: | - YAML.add_domain_type( "domain.tld,2002", /(invoice|customer)/ ) { |type, val| - if val.is_a? ::Hash - scheme, domain, type = type.split( /:/, 3 ) - val['type'] = "domain #{type}" - val - else - raise YAML::Error, "Not a Hash in domain.tld/invoice: " + val.inspect - end - } -ruby: | - { "invoice"=> { "customers"=> [ { "given"=>"Chris", "type"=>"domain customer", "family"=>"Dumars" } ], "type"=>"domain invoice" } } - ---- -test: Overriding anchors -yaml: | - anchor : &A001 This scalar has an anchor. - override : &A001 > - The alias node below is a - repeated use of this value. - alias : *A001 -php: | - array( 'anchor' => 'This scalar has an anchor.', - 'override' => "The alias node below is a repeated use of this value.\n", - 'alias' => "The alias node below is a repeated use of this value.\n" ) ---- -test: Flow and block formatting -todo: true -yaml: | - empty: [] - flow: [ one, two, three # May span lines, - , four, # indentation is - five ] # mostly ignored. - block: - - First item in top sequence - - - - Subordinate sequence entry - - > - A folded sequence entry - - Sixth item in top sequence -ruby: | - { 'empty' => [], 'flow' => [ 'one', 'two', 'three', 'four', 'five' ], - 'block' => [ 'First item in top sequence', [ 'Subordinate sequence entry' ], - "A folded sequence entry\n", 'Sixth item in top sequence' ] } ---- -test: Complete mapping test -todo: true -yaml: | - empty: {} - flow: { one: 1, two: 2 } - spanning: { one: 1, - two: 2 } - block: - first : First entry - second: - key: Subordinate mapping - third: - - Subordinate sequence - - { } - - Previous mapping is empty. - - A key: value pair in a sequence. - A second: key:value pair. - - The previous entry is equal to the following one. - - - A key: value pair in a sequence. - A second: key:value pair. - !float 12 : This key is a float. - ? > - ? - : This key had to be protected. - "\a" : This key had to be escaped. - ? > - This is a - multi-line - folded key - : Whose value is - also multi-line. - ? this also works as a key - : with a value at the next line. - ? - - This key - - is a sequence - : - - With a sequence value. - ? - This: key - is a: mapping - : - with a: mapping value. -ruby: | - { 'empty' => {}, 'flow' => { 'one' => 1, 'two' => 2 }, - 'spanning' => { 'one' => 1, 'two' => 2 }, - 'block' => { 'first' => 'First entry', 'second' => - { 'key' => 'Subordinate mapping' }, 'third' => - [ 'Subordinate sequence', {}, 'Previous mapping is empty.', - { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' }, - 'The previous entry is equal to the following one.', - { 'A key' => 'value pair in a sequence.', 'A second' => 'key:value pair.' } ], - 12.0 => 'This key is a float.', "?\n" => 'This key had to be protected.', - "\a" => 'This key had to be escaped.', - "This is a multi-line folded key\n" => "Whose value is also multi-line.", - 'this also works as a key' => 'with a value at the next line.', - [ 'This key', 'is a sequence' ] => [ 'With a sequence value.' ] } } - # Couldn't recreate map exactly, so we'll do a detailed check to be sure it's entact - obj_y['block'].keys.each { |k| - if Hash === k - v = obj_y['block'][k] - if k['This'] == 'key' and k['is a'] == 'mapping' and v['with a'] == 'mapping value.' - obj_r['block'][k] = v - end - end - } ---- -test: Literal explicit indentation -yaml: | - # Explicit indentation must - # be given in all the three - # following cases. - leading spaces: |2 - This value starts with four spaces. - - leading line break: |2 - - This value starts with a line break. - - leading comment indicator: |2 - # first line starts with a - # character. - - # Explicit indentation may - # also be given when it is - # not required. - redundant: |2 - This value is indented 2 spaces. -php: | - array( - 'leading spaces' => " This value starts with four spaces.\n", - 'leading line break' => "\nThis value starts with a line break.\n", - 'leading comment indicator' => "# first line starts with a\n# character.\n", - 'redundant' => "This value is indented 2 spaces.\n" - ) ---- -test: Chomping and keep modifiers -yaml: | - clipped: | - This has one newline. - - same as "clipped" above: "This has one newline.\n" - - stripped: |- - This has no newline. - - same as "stripped" above: "This has no newline." - - kept: |+ - This has two newlines. - - same as "kept" above: "This has two newlines.\n\n" -php: | - array( - 'clipped' => "This has one newline.\n", - 'same as "clipped" above' => "This has one newline.\n", - 'stripped' => 'This has no newline.', - 'same as "stripped" above' => 'This has no newline.', - 'kept' => "This has two newlines.\n\n", - 'same as "kept" above' => "This has two newlines.\n\n" - ) ---- -test: Literal combinations -todo: true -yaml: | - empty: | - - literal: | - The \ ' " characters may be - freely used. Leading white - space is significant. - - Line breaks are significant. - Thus this value contains one - empty line and ends with a - single line break, but does - not start with one. - - is equal to: "The \\ ' \" characters may \ - be\nfreely used. Leading white\n space \ - is significant.\n\nLine breaks are \ - significant.\nThus this value contains \ - one\nempty line and ends with a\nsingle \ - line break, but does\nnot start with one.\n" - - # Comments may follow a block - # scalar value. They must be - # less indented. - - # Modifiers may be combined in any order. - indented and chomped: |2- - This has no newline. - - also written as: |-2 - This has no newline. - - both are equal to: " This has no newline." -php: | - array( - 'empty' => '', - 'literal' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + - "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + - "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", - 'is equal to' => "The \\ ' \" characters may be\nfreely used. Leading white\n space " + - "is significant.\n\nLine breaks are significant.\nThus this value contains one\n" + - "empty line and ends with a\nsingle line break, but does\nnot start with one.\n", - 'indented and chomped' => ' This has no newline.', - 'also written as' => ' This has no newline.', - 'both are equal to' => ' This has no newline.' - ) ---- -test: Folded combinations -todo: true -yaml: | - empty: > - - one paragraph: > - Line feeds are converted - to spaces, so this value - contains no line breaks - except for the final one. - - multiple paragraphs: >2 - - An empty line, either - at the start or in - the value: - - Is interpreted as a - line break. Thus this - value contains three - line breaks. - - indented text: > - This is a folded - paragraph followed - by a list: - * first entry - * second entry - Followed by another - folded paragraph, - another list: - - * first entry - - * second entry - - And a final folded - paragraph. - - above is equal to: | - This is a folded paragraph followed by a list: - * first entry - * second entry - Followed by another folded paragraph, another list: - - * first entry - - * second entry - - And a final folded paragraph. - - # Explicit comments may follow - # but must be less indented. -php: | - array( - 'empty' => '', - 'one paragraph' => 'Line feeds are converted to spaces, so this value'. - " contains no line breaks except for the final one.\n", - 'multiple paragraphs' => "\nAn empty line, either at the start or in the value:\n". - "Is interpreted as a line break. Thus this value contains three line breaks.\n", - 'indented text' => "This is a folded paragraph followed by a list:\n". - " * first entry\n * second entry\nFollowed by another folded paragraph, ". - "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n", - 'above is equal to' => "This is a folded paragraph followed by a list:\n". - " * first entry\n * second entry\nFollowed by another folded paragraph, ". - "another list:\n\n * first entry\n\n * second entry\n\nAnd a final folded paragraph.\n" - ) ---- -test: Single quotes -todo: true -yaml: | - empty: '' - second: '! : \ etc. can be used freely.' - third: 'a single quote '' must be escaped.' - span: 'this contains - six spaces - - and one - line break' - is same as: "this contains six spaces\nand one line break" -php: | - array( - 'empty' => '', - 'second' => '! : \\ etc. can be used freely.', - 'third' => "a single quote ' must be escaped.", - 'span' => "this contains six spaces\nand one line break", - 'is same as' => "this contains six spaces\nand one line break" - ) ---- -test: Double quotes -todo: true -yaml: | - empty: "" - second: "! : etc. can be used freely." - third: "a \" or a \\ must be escaped." - fourth: "this value ends with an LF.\n" - span: "this contains - four \ - spaces" - is equal to: "this contains four spaces" -php: | - array( - 'empty' => '', - 'second' => '! : etc. can be used freely.', - 'third' => 'a " or a \\ must be escaped.', - 'fourth' => "this value ends with an LF.\n", - 'span' => "this contains four spaces", - 'is equal to' => "this contains four spaces" - ) ---- -test: Unquoted strings -todo: true -yaml: | - first: There is no unquoted empty string. - - second: 12 ## This is an integer. - - third: !str 12 ## This is a string. - - span: this contains - six spaces - - and one - line break - - indicators: this has no comments. - #:foo and bar# are - both text. - - flow: [ can span - lines, # comment - like - this ] - - note: { one-line keys: but multi-line values } - -php: | - array( - 'first' => 'There is no unquoted empty string.', - 'second' => 12, - 'third' => '12', - 'span' => "this contains six spaces\nand one line break", - 'indicators' => "this has no comments. #:foo and bar# are both text.", - 'flow' => [ 'can span lines', 'like this' ], - 'note' => { 'one-line keys' => 'but multi-line values' } - ) ---- -test: Spanning sequences -todo: true -yaml: | - # The following are equal seqs - # with different identities. - flow: [ one, two ] - spanning: [ one, - two ] - block: - - one - - two -php: | - array( - 'flow' => [ 'one', 'two' ], - 'spanning' => [ 'one', 'two' ], - 'block' => [ 'one', 'two' ] - ) ---- -test: Flow mappings -yaml: | - # The following are equal maps - # with different identities. - flow: { one: 1, two: 2 } - block: - one: 1 - two: 2 -php: | - array( - 'flow' => array( 'one' => 1, 'two' => 2 ), - 'block' => array( 'one' => 1, 'two' => 2 ) - ) ---- -test: Representations of 12 -todo: true -yaml: | - - 12 # An integer - # The following scalars - # are loaded to the - # string value '1' '2'. - - !str 12 - - '12' - - "12" - - "\ - 1\ - 2\ - " - # Strings containing paths and regexps can be unquoted: - - /foo/bar - - d:/foo/bar - - foo/bar - - /a.*b/ -php: | - array( 12, '12', '12', '12', '12', '/foo/bar', 'd:/foo/bar', 'foo/bar', '/a.*b/' ) ---- -test: "Null" -todo: true -yaml: | - canonical: ~ - - english: null - - # This sequence has five - # entries, two with values. - sparse: - - ~ - - 2nd entry - - Null - - 4th entry - - - - four: This mapping has five keys, - only two with values. - -php: | - array ( - 'canonical' => null, - 'english' => null, - 'sparse' => array( null, '2nd entry', null, '4th entry', null ]), - 'four' => 'This mapping has five keys, only two with values.' - ) ---- -test: Omap -todo: true -yaml: | - # Explicitly typed dictionary. - Bestiary: !omap - - aardvark: African pig-like ant eater. Ugly. - - anteater: South-American ant eater. Two species. - - anaconda: South-American constrictor snake. Scary. - # Etc. -ruby: | - { - 'Bestiary' => YAML::Omap[ - 'aardvark', 'African pig-like ant eater. Ugly.', - 'anteater', 'South-American ant eater. Two species.', - 'anaconda', 'South-American constrictor snake. Scary.' - ] - } - ---- -test: Pairs -todo: true -yaml: | - # Explicitly typed pairs. - tasks: !pairs - - meeting: with team. - - meeting: with boss. - - break: lunch. - - meeting: with client. -ruby: | - { - 'tasks' => YAML::Pairs[ - 'meeting', 'with team.', - 'meeting', 'with boss.', - 'break', 'lunch.', - 'meeting', 'with client.' - ] - } - ---- -test: Set -todo: true -yaml: | - # Explicitly typed set. - baseball players: !set - Mark McGwire: - Sammy Sosa: - Ken Griffey: -ruby: | - { - 'baseball players' => YAML::Set[ - 'Mark McGwire', nil, - 'Sammy Sosa', nil, - 'Ken Griffey', nil - ] - } - ---- -test: Boolean -yaml: | - false: used as key - logical: true - answer: false -php: | - array( - false => 'used as key', - 'logical' => true, - 'answer' => false - ) ---- -test: Integer -yaml: | - canonical: 12345 - decimal: +12,345 - octal: 014 - hexadecimal: 0xC -php: | - array( - 'canonical' => 12345, - 'decimal' => 12345, - 'octal' => 12, - 'hexadecimal' => 12 - ) ---- -test: Float -yaml: | - canonical: 1.23015e+3 - exponential: 12.3015e+02 - fixed: 1,230.15 - negative infinity: -.inf - not a number: .NaN -php: | - array( - 'canonical' => 1230.15, - 'exponential' => 1230.15, - 'fixed' => 1230.15, - 'negative infinity' => log(0), - 'not a number' => -log(0) - ) ---- -test: Timestamp -todo: true -yaml: | - canonical: 2001-12-15T02:59:43.1Z - valid iso8601: 2001-12-14t21:59:43.10-05:00 - space separated: 2001-12-14 21:59:43.10 -05:00 - date (noon UTC): 2002-12-14 -ruby: | - array( - 'canonical' => YAML::mktime( 2001, 12, 15, 2, 59, 43, 0.10 ), - 'valid iso8601' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'space separated' => YAML::mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'date (noon UTC)' => Date.new( 2002, 12, 14 ) - ) ---- -test: Binary -todo: true -yaml: | - canonical: !binary "\ - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\ - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\ - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=" - base64: !binary | - R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5 - OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+ - +f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC - AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs= - description: > - The binary value above is a tiny arrow - encoded as a gif image. -ruby-setup: | - arrow_gif = "GIF89a\f\000\f\000\204\000\000\377\377\367\365\365\356\351\351\345fff\000\000\000\347\347\347^^^\363\363\355\216\216\216\340\340\340\237\237\237\223\223\223\247\247\247\236\236\236iiiccc\243\243\243\204\204\204\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371\377\376\371!\376\016Made with GIMP\000,\000\000\000\000\f\000\f\000\000\005, \216\2010\236\343@\024\350i\020\304\321\212\010\034\317\200M$z\357\3770\205p\270\2601f\r\e\316\001\303\001\036\020' \202\n\001\000;" -ruby: | - { - 'canonical' => arrow_gif, - 'base64' => arrow_gif, - 'description' => "The binary value above is a tiny arrow encoded as a gif image.\n" - } - ---- -test: Merge key -todo: true -yaml: | - --- - - &CENTER { x: 1, y: 2 } - - &LEFT { x: 0, y: 2 } - - &BIG { r: 10 } - - &SMALL { r: 1 } - - # All the following maps are equal: - - - # Explicit keys - x: 1 - y: 2 - r: 10 - label: center/big - - - # Merge one map - << : *CENTER - r: 10 - label: center/big - - - # Merge multiple maps - << : [ *CENTER, *BIG ] - label: center/big - - - # Override - << : [ *BIG, *LEFT, *SMALL ] - x: 1 - label: center/big - -ruby-setup: | - center = { 'x' => 1, 'y' => 2 } - left = { 'x' => 0, 'y' => 2 } - big = { 'r' => 10 } - small = { 'r' => 1 } - node1 = { 'x' => 1, 'y' => 2, 'r' => 10, 'label' => 'center/big' } - node2 = center.dup - node2.update( { 'r' => 10, 'label' => 'center/big' } ) - node3 = big.dup - node3.update( center ) - node3.update( { 'label' => 'center/big' } ) - node4 = small.dup - node4.update( left ) - node4.update( big ) - node4.update( { 'x' => 1, 'label' => 'center/big' } ) - -ruby: | - [ - center, left, big, small, node1, node2, node3, node4 - ] - ---- -test: Default key -todo: true -yaml: | - --- # Old schema - link with: - - library1.dll - - library2.dll - --- # New schema - link with: - - = : library1.dll - version: 1.2 - - = : library2.dll - version: 2.3 -ruby: | - y = YAML::Stream.new - y.add( { 'link with' => [ 'library1.dll', 'library2.dll' ] } ) - obj_h = Hash[ 'version' => 1.2 ] - obj_h.default = 'library1.dll' - obj_h2 = Hash[ 'version' => 2.3 ] - obj_h2.default = 'library2.dll' - y.add( { 'link with' => [ obj_h, obj_h2 ] } ) -documents: 2 - ---- -test: Special keys -todo: true -yaml: | - "!": These three keys - "&": had to be quoted - "=": and are normal strings. - # NOTE: the following node should NOT be serialized this way. - encoded node : - !special '!' : '!type' - !special|canonical '&' : 12 - = : value - # The proper way to serialize the above node is as follows: - node : !!type &12 value -ruby: | - { '!' => 'These three keys', '&' => 'had to be quoted', - '=' => 'and are normal strings.', - 'encoded node' => YAML::PrivateType.new( 'type', 'value' ), - 'node' => YAML::PrivateType.new( 'type', 'value' ) } diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml deleted file mode 100644 index aac4e6807..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/YtsTypeTransfers.yml +++ /dev/null @@ -1,244 +0,0 @@ ---- %YAML:1.0 -test: Strings -brief: > - Any group of characters beginning with an - alphabetic or numeric character is a string, - unless it belongs to one of the groups below - (such as an Integer or Time). -yaml: | - String -php: | - 'String' ---- -test: String characters -brief: > - A string can contain any alphabetic or - numeric character, along with many - punctuation characters, including the - period, dash, space, quotes, exclamation, and - question mark. -yaml: | - - What's Yaml? - - It's for writing data structures in plain text. - - And? - - And what? That's not good enough for you? - - No, I mean, "And what about Yaml?" - - Oh, oh yeah. Uh.. Yaml for Ruby. -php: | - array( - "What's Yaml?", - "It's for writing data structures in plain text.", - "And?", - "And what? That's not good enough for you?", - "No, I mean, \"And what about Yaml?\"", - "Oh, oh yeah. Uh.. Yaml for Ruby." - ) ---- -test: Indicators in Strings -brief: > - Be careful using indicators in strings. In particular, - the comma, colon, and pound sign must be used carefully. -yaml: | - the colon followed by space is an indicator: but is a string:right here - same for the pound sign: here we have it#in a string - the comma can, honestly, be used in most cases: [ but not in, inline collections ] -php: | - array( - 'the colon followed by space is an indicator' => 'but is a string:right here', - 'same for the pound sign' => 'here we have it#in a string', - 'the comma can, honestly, be used in most cases' => array('but not in', 'inline collections') - ) ---- -test: Forcing Strings -brief: > - Any YAML type can be forced into a string using the - explicit !str method. -yaml: | - date string: !str 2001-08-01 - number string: !str 192 -php: | - array( - 'date string' => '2001-08-01', - 'number string' => '192' - ) ---- -test: Single-quoted Strings -brief: > - You can also enclose your strings within single quotes, - which allows use of slashes, colons, and other indicators - freely. Inside single quotes, you can represent a single - quote in your string by using two single quotes next to - each other. -yaml: | - all my favorite symbols: '#:!/%.)' - a few i hate: '&(*' - why do i hate them?: 'it''s very hard to explain' - entities: '£ me' -php: | - array( - 'all my favorite symbols' => '#:!/%.)', - 'a few i hate' => '&(*', - 'why do i hate them?' => 'it\'s very hard to explain', - 'entities' => '£ me' - ) ---- -test: Double-quoted Strings -brief: > - Enclosing strings in double quotes allows you - to use escapings to represent ASCII and - Unicode characters. -yaml: | - i know where i want my line breaks: "one here\nand another here\n" -php: | - array( - 'i know where i want my line breaks' => "one here\nand another here\n" - ) ---- -test: Multi-line Quoted Strings -todo: true -brief: > - Both single- and double-quoted strings may be - carried on to new lines in your YAML document. - They must be indented a step and indentation - is interpreted as a single space. -yaml: | - i want a long string: "so i'm going to - let it go on and on to other lines - until i end it with a quote." -php: | - array('i want a long string' => "so i'm going to ". - "let it go on and on to other lines ". - "until i end it with a quote." - ) - ---- -test: Plain scalars -todo: true -brief: > - Unquoted strings may also span multiple lines, if they - are free of YAML space indicators and indented. -yaml: | - - My little toe is broken in two places; - - I'm crazy to have skied this way; - - I'm not the craziest he's seen, since there was always the German guy - who skied for 3 hours on a broken shin bone (just below the kneecap); - - Nevertheless, second place is respectable, and he doesn't - recommend going for the record; - - He's going to put my foot in plaster for a month; - - This would impair my skiing ability somewhat for the - duration, as can be imagined. -php: | - array( - "My little toe is broken in two places;", - "I'm crazy to have skied this way;", - "I'm not the craziest he's seen, since there was always ". - "the German guy who skied for 3 hours on a broken shin ". - "bone (just below the kneecap);", - "Nevertheless, second place is respectable, and he doesn't ". - "recommend going for the record;", - "He's going to put my foot in plaster for a month;", - "This would impair my skiing ability somewhat for the duration, ". - "as can be imagined." - ) ---- -test: 'Null' -brief: > - You can use the tilde '~' character for a null value. -yaml: | - name: Mr. Show - hosted by: Bob and David - date of next season: ~ -php: | - array( - 'name' => 'Mr. Show', - 'hosted by' => 'Bob and David', - 'date of next season' => null - ) ---- -test: Boolean -brief: > - You can use 'true' and 'false' for Boolean values. -yaml: | - Is Gus a Liar?: true - Do I rely on Gus for Sustenance?: false -php: | - array( - 'Is Gus a Liar?' => true, - 'Do I rely on Gus for Sustenance?' => false - ) ---- -test: Integers -dump_skip: true -brief: > - An integer is a series of numbers, optionally - starting with a positive or negative sign. Integers - may also contain commas for readability. -yaml: | - zero: 0 - simple: 12 - one-thousand: 1,000 - negative one-thousand: -1,000 -php: | - array( - 'zero' => 0, - 'simple' => 12, - 'one-thousand' => 1000, - 'negative one-thousand' => -1000 - ) ---- -test: Integers as Map Keys -brief: > - An integer can be used a dictionary key. -yaml: | - 1: one - 2: two - 3: three -php: | - array( - 1 => 'one', - 2 => 'two', - 3 => 'three' - ) ---- -test: Floats -dump_skip: true -brief: > - Floats are represented by numbers with decimals, - allowing for scientific notation, as well as - positive and negative infinity and "not a number." -yaml: | - a simple float: 2.00 - larger float: 1,000.09 - scientific notation: 1.00009e+3 -php: | - array( - 'a simple float' => 2.0, - 'larger float' => 1000.09, - 'scientific notation' => 1000.09 - ) ---- -test: Time -todo: true -brief: > - You can represent timestamps by using - ISO8601 format, or a variation which - allows spaces between the date, time and - time zone. -yaml: | - iso8601: 2001-12-14t21:59:43.10-05:00 - space separated: 2001-12-14 21:59:43.10 -05:00 -php: | - array( - 'iso8601' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ), - 'space separated' => mktime( 2001, 12, 14, 21, 59, 43, 0.10, "-05:00" ) - ) ---- -test: Date -todo: true -brief: > - A date can be represented by its year, - month and day in ISO8601 order. -yaml: | - 1976-07-31 -php: | - date( 1976, 7, 31 ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml deleted file mode 100644 index ec456ed09..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/embededPhp.yml +++ /dev/null @@ -1 +0,0 @@ -value: diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml deleted file mode 100644 index 09bf86e79..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/escapedCharacters.yml +++ /dev/null @@ -1,147 +0,0 @@ -test: outside double quotes -yaml: | - \0 \ \a \b \n -php: | - "\\0 \\ \\a \\b \\n" ---- -test: null -yaml: | - "\0" -php: | - "\x00" ---- -test: bell -yaml: | - "\a" -php: | - "\x07" ---- -test: backspace -yaml: | - "\b" -php: | - "\x08" ---- -test: horizontal tab (1) -yaml: | - "\t" -php: | - "\x09" ---- -test: horizontal tab (2) -yaml: | - "\ " -php: | - "\x09" ---- -test: line feed -yaml: | - "\n" -php: | - "\x0a" ---- -test: vertical tab -yaml: | - "\v" -php: | - "\x0b" ---- -test: form feed -yaml: | - "\f" -php: | - "\x0c" ---- -test: carriage return -yaml: | - "\r" -php: | - "\x0d" ---- -test: escape -yaml: | - "\e" -php: | - "\x1b" ---- -test: space -yaml: | - "\ " -php: | - "\x20" ---- -test: slash -yaml: | - "\/" -php: | - "\x2f" ---- -test: backslash -yaml: | - "\\" -php: | - "\\" ---- -test: Unicode next line -yaml: | - "\N" -php: | - "\xc2\x85" ---- -test: Unicode non-breaking space -yaml: | - "\_" -php: | - "\xc2\xa0" ---- -test: Unicode line separator -yaml: | - "\L" -php: | - "\xe2\x80\xa8" ---- -test: Unicode paragraph separator -yaml: | - "\P" -php: | - "\xe2\x80\xa9" ---- -test: Escaped 8-bit Unicode -yaml: | - "\x42" -php: | - "B" ---- -test: Escaped 16-bit Unicode -yaml: | - "\u20ac" -php: | - "\xe2\x82\xac" ---- -test: Escaped 32-bit Unicode -yaml: | - "\U00000043" -php: | - "C" ---- -test: Example 5.13 Escaped Characters -note: | - Currently throws an error parsing first line. Maybe Symfony Yaml doesn't support - continuation of string across multiple lines? Keeping test here but disabled. -todo: true -yaml: | - "Fun with \\ - \" \a \b \e \f \ - \n \r \t \v \0 \ - \ \_ \N \L \P \ - \x41 \u0041 \U00000041" -php: | - "Fun with \x5C\n\x22 \x07 \x08 \x1B \x0C\n\x0A \x0D \x09 \x0B \x00\n\x20 \xA0 \x85 \xe2\x80\xa8 \xe2\x80\xa9\nA A A" ---- -test: Double quotes with a line feed -yaml: | - { double: "some value\n \"some quoted string\" and 'some single quotes one'" } -php: | - array( - 'double' => "some value\n \"some quoted string\" and 'some single quotes one'" - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml deleted file mode 100644 index 3216a89eb..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/index.yml +++ /dev/null @@ -1,18 +0,0 @@ -- escapedCharacters -- sfComments -- sfCompact -- sfTests -- sfObjects -- sfMergeKey -- sfQuotes -- YtsAnchorAlias -- YtsBasicTests -- YtsBlockMapping -- YtsDocumentSeparator -- YtsErrorTests -- YtsFlowCollections -- YtsFoldedScalars -- YtsNullsAndEmpties -- YtsSpecificationExamples -- YtsTypeTransfers -- unindentedCollections diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml deleted file mode 100644 index 46addfcd3..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfComments.yml +++ /dev/null @@ -1,65 +0,0 @@ ---- %YAML:1.0 -test: Comments at the end of a line -brief: > - Comments at the end of a line -yaml: | - ex1: "foo # bar" - ex2: "foo # bar" # comment - ex3: 'foo # bar' # comment - ex4: foo # comment -php: | - array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') ---- -test: Comments in the middle -brief: > - Comments in the middle -yaml: | - foo: - # some comment - # some comment - bar: foo - # some comment - # some comment -php: | - array('foo' => array('bar' => 'foo')) ---- -test: Comments on a hash line -brief: > - Comments on a hash line -yaml: | - foo: # a comment - foo: bar # a comment -php: | - array('foo' => array('foo' => 'bar')) ---- -test: 'Value starting with a #' -brief: > - 'Value starting with a #' -yaml: | - foo: '#bar' -php: | - array('foo' => '#bar') ---- -test: Document starting with a comment and a separator -brief: > - Commenting before document start is allowed -yaml: | - # document comment - --- - foo: bar # a comment -php: | - array('foo' => 'bar') ---- -test: Comment containing a colon on a hash line -brief: > - Comment containing a colon on a scalar line -yaml: 'foo # comment: this is also part of the comment' -php: | - 'foo' ---- -test: 'Hash key containing a #' -brief: > - 'Hash key containing a #' -yaml: 'foo#bar: baz' -php: | - array('foo#bar' => 'baz') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml deleted file mode 100644 index 1339d23a6..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfCompact.yml +++ /dev/null @@ -1,159 +0,0 @@ ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) ---- %YAML:1.0 -test: Compact notation -brief: | - Compact notation for sets of mappings with single element -yaml: | - --- - # products purchased - - item : Super Hoop - - item : Basketball - quantity: 1 - - item: - name: Big Shoes - nick: Biggies - quantity: 1 -php: | - array ( - array ( - 'item' => 'Super Hoop', - ), - array ( - 'item' => 'Basketball', - 'quantity' => 1, - ), - array ( - 'item' => array( - 'name' => 'Big Shoes', - 'nick' => 'Biggies' - ), - 'quantity' => 1 - ) - ) ---- -test: Compact notation combined with inline notation -brief: | - Combinations of compact and inline notation are allowed -yaml: | - --- - items: - - { item: Super Hoop, quantity: 1 } - - [ Basketball, Big Shoes ] -php: | - array ( - 'items' => array ( - array ( - 'item' => 'Super Hoop', - 'quantity' => 1, - ), - array ( - 'Basketball', - 'Big Shoes' - ) - ) - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml deleted file mode 100644 index 3eec4f877..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfMergeKey.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- %YAML:1.0 -test: Simple In Place Substitution -brief: > - If you want to reuse an entire alias, only overwriting what is different - you can use a << in place substitution. This is not part of the official - YAML spec, but a widely implemented extension. See the following URL for - details: http://yaml.org/type/merge.html -yaml: | - foo: &foo - a: Steve - b: Clark - c: Brian - bar: &bar - <<: *foo - x: Oren - foo2: &foo2 - a: Ballmer - ding: &dong [ fi, fei, fo, fam] - check: - <<: - - *foo - - *dong - isit: tested - head: - <<: [ *foo , *dong , *foo2 ] -php: | - array('foo' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian'), 'bar' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'x' => 'Oren'), 'foo2' => array('a' => 'Ballmer'), 'ding' => array('fi', 'fei', 'fo', 'fam'), 'check' => array('a' => 'Steve', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam', 'isit' => 'tested'), 'head' => array('a' => 'Ballmer', 'b' => 'Clark', 'c' => 'Brian', 'fi', 'fei', 'fo', 'fam')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml deleted file mode 100644 index ee124b244..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfObjects.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- %YAML:1.0 -test: Objects -brief: > - Comments at the end of a line -yaml: | - ex1: "foo # bar" - ex2: "foo # bar" # comment - ex3: 'foo # bar' # comment - ex4: foo # comment -php: | - array('ex1' => 'foo # bar', 'ex2' => 'foo # bar', 'ex3' => 'foo # bar', 'ex4' => 'foo') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml deleted file mode 100644 index 741f1befe..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfQuotes.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- %YAML:1.0 -test: Some characters at the beginning of a string must be escaped -brief: > - Some characters at the beginning of a string must be escaped -yaml: | - foo: | bar -php: | - array('foo' => '| bar') ---- -test: A key can be a quoted string -brief: > - A key can be a quoted string -yaml: | - "foo1": bar - 'foo2': bar - "foo \" bar": bar - 'foo '' bar': bar - 'foo3: ': bar - "foo4: ": bar - foo5: { "foo \" bar: ": bar, 'foo '' bar: ': bar } -php: | - array( - 'foo1' => 'bar', - 'foo2' => 'bar', - 'foo " bar' => 'bar', - 'foo \' bar' => 'bar', - 'foo3: ' => 'bar', - 'foo4: ' => 'bar', - 'foo5' => array( - 'foo " bar: ' => 'bar', - 'foo \' bar: ' => 'bar', - ), - ) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml deleted file mode 100644 index 7a54f1639..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/sfTests.yml +++ /dev/null @@ -1,135 +0,0 @@ ---- %YAML:1.0 -test: Multiple quoted string on one line -brief: > - Multiple quoted string on one line -yaml: | - stripped_title: { name: "foo bar", help: "bar foo" } -php: | - array('stripped_title' => array('name' => 'foo bar', 'help' => 'bar foo')) ---- -test: Empty sequence -yaml: | - foo: [ ] -php: | - array('foo' => array()) ---- -test: Empty value -yaml: | - foo: -php: | - array('foo' => null) ---- -test: Inline string parsing -brief: > - Inline string parsing -yaml: | - test: ['complex: string', 'another [string]'] -php: | - array('test' => array('complex: string', 'another [string]')) ---- -test: Boolean -brief: > - Boolean -yaml: | - - false - - true - - null - - ~ - - 'false' - - 'true' - - 'null' - - '~' -php: | - array( - false, - true, - null, - null, - 'false', - 'true', - 'null', - '~', - ) ---- -test: Empty lines in folded blocks -brief: > - Empty lines in folded blocks -yaml: | - foo: - bar: | - foo - - - - bar -php: | - array('foo' => array('bar' => "foo\n\n\n \nbar\n")) ---- -test: IP addresses -brief: > - IP addresses -yaml: | - foo: 10.0.0.2 -php: | - array('foo' => '10.0.0.2') ---- -test: A sequence with an embedded mapping -brief: > - A sequence with an embedded mapping -yaml: | - - foo - - bar: { bar: foo } -php: | - array('foo', array('bar' => array('bar' => 'foo'))) ---- -test: A sequence with an unordered array -brief: > - A sequence with an unordered array -yaml: | - 1: foo - 0: bar -php: | - array(1 => 'foo', 0 => 'bar') ---- -test: Octal -brief: as in spec example 2.19, octal value is converted -yaml: | - foo: 0123 -php: | - array('foo' => 83) ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: "0123" -php: | - array('foo' => '0123') ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: '0123' -php: | - array('foo' => '0123') ---- -test: Octal strings -brief: Octal notation in a string must remain a string -yaml: | - foo: | - 0123 -php: | - array('foo' => "0123\n") ---- -test: Document as a simple hash -brief: Document as a simple hash -yaml: | - { foo: bar } -php: | - array('foo' => 'bar') ---- -test: Document as a simple array -brief: Document as a simple array -yaml: | - [ foo, bar ] -php: | - array('foo', 'bar') diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml deleted file mode 100644 index fd8ad7ed4..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/Fixtures/unindentedCollections.yml +++ /dev/null @@ -1,62 +0,0 @@ ---- %YAML:1.0 -test: Unindented collection -brief: > - Unindented collection -yaml: | - collection: - - item1 - - item2 - - item3 -php: | - array('collection' => array('item1', 'item2', 'item3')) ---- -test: Nested unindented collection (two levels) -brief: > - Nested unindented collection -yaml: | - collection: - key: - - a - - b - - c -php: | - array('collection' => array('key' => array('a', 'b', 'c'))) ---- -test: Nested unindented collection (three levels) -brief: > - Nested unindented collection -yaml: | - collection: - key: - subkey: - - one - - two - - three -php: | - array('collection' => array('key' => array('subkey' => array('one', 'two', 'three')))) ---- -test: Key/value after unindented collection (1) -brief: > - Key/value after unindented collection (1) -yaml: | - collection: - key: - - a - - b - - c - foo: bar -php: | - array('collection' => array('key' => array('a', 'b', 'c')), 'foo' => 'bar') ---- -test: Key/value after unindented collection (at the same level) -brief: > - Key/value after unindented collection -yaml: | - collection: - key: - - a - - b - - c - foo: bar -php: | - array('collection' => array('key' => array('a', 'b', 'c'), 'foo' => 'bar')) diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php deleted file mode 100644 index dc497ca21..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/InlineTest.php +++ /dev/null @@ -1,231 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Inline; - -class InlineTest extends \PHPUnit_Framework_TestCase -{ - public function testParse() - { - foreach ($this->getTestsForParse() as $yaml => $value) { - $this->assertSame($value, Inline::parse($yaml), sprintf('::parse() converts an inline YAML to a PHP structure (%s)', $yaml)); - } - } - - public function testDump() - { - $testsForDump = $this->getTestsForDump(); - - foreach ($testsForDump as $yaml => $value) { - $this->assertEquals($yaml, Inline::dump($value), sprintf('::dump() converts a PHP structure to an inline YAML (%s)', $yaml)); - } - - foreach ($this->getTestsForParse() as $value) { - $this->assertEquals($value, Inline::parse(Inline::dump($value)), 'check consistency'); - } - - foreach ($testsForDump as $value) { - $this->assertEquals($value, Inline::parse(Inline::dump($value)), 'check consistency'); - } - } - - public function testDumpNumericValueWithLocale() - { - $locale = setlocale(LC_NUMERIC, 0); - if (false === $locale) { - $this->markTestSkipped('Your platform does not support locales.'); - } - - $required_locales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252'); - if (false === setlocale(LC_ALL, $required_locales)) { - $this->markTestSkipped('Could not set any of required locales: '.implode(", ", $required_locales)); - } - - $this->assertEquals('1.2', Inline::dump(1.2)); - $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0))); - - setlocale(LC_ALL, $locale); - } - - public function testHashStringsResemblingExponentialNumericsShouldNotBeChangedToINF() - { - $value = '686e444'; - - $this->assertSame($value, Inline::parse(Inline::dump($value))); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseScalarWithIncorrectlyQuotedStringShouldThrowException() - { - $value = "'don't do somthin' like that'"; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseScalarWithIncorrectlyDoubleQuotedStringShouldThrowException() - { - $value = '"don"t do somthin" like that"'; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidMappingKeyShouldThrowException() - { - $value = '{ "foo " bar": "bar" }'; - Inline::parse($value); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidMappingShouldThrowException() - { - Inline::parse('[foo] bar'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testParseInvalidSequenceShouldThrowException() - { - Inline::parse('{ foo: bar } bar'); - } - - public function testParseScalarWithCorrectlyQuotedStringShouldReturnString() - { - $value = "'don''t do somthin'' like that'"; - $expect = "don't do somthin' like that"; - - $this->assertSame($expect, Inline::parseScalar($value)); - } - - protected function getTestsForParse() - { - return array( - '' => '', - 'null' => null, - 'false' => false, - 'true' => true, - '12' => 12, - '-12' => -12, - '"quoted string"' => 'quoted string', - "'quoted string'" => 'quoted string', - '12.30e+02' => 12.30e+02, - '0x4D2' => 0x4D2, - '02333' => 02333, - '.Inf' => -log(0), - '-.Inf' => log(0), - "'686e444'" => '686e444', - '686e444' => 646e444, - '123456789123456789123456789123456789' => '123456789123456789123456789123456789', - '"foo\r\nbar"' => "foo\r\nbar", - "'foo#bar'" => 'foo#bar', - "'foo # bar'" => 'foo # bar', - "'#cfcfcf'" => '#cfcfcf', - '::form_base.html.twig' => '::form_base.html.twig', - - '2007-10-30' => mktime(0, 0, 0, 10, 30, 2007), - '2007-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 2007), - '2007-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 2007), - '1960-10-30 02:59:43 Z' => gmmktime(2, 59, 43, 10, 30, 1960), - '1730-10-30T02:59:43Z' => gmmktime(2, 59, 43, 10, 30, 1730), - - '"a \\"string\\" with \'quoted strings inside\'"' => 'a "string" with \'quoted strings inside\'', - "'a \"string\" with ''quoted strings inside'''" => 'a "string" with \'quoted strings inside\'', - - // sequences - // urls are no key value mapping. see #3609. Valid yaml "key: value" mappings require a space after the colon - '[foo, http://urls.are/no/mappings, false, null, 12]' => array('foo', 'http://urls.are/no/mappings', false, null, 12), - '[ foo , bar , false , null , 12 ]' => array('foo', 'bar', false, null, 12), - '[\'foo,bar\', \'foo bar\']' => array('foo,bar', 'foo bar'), - - // mappings - '{foo:bar,bar:foo,false:false,null:null,integer:12}' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), - '{ foo : bar, bar : foo, false : false, null : null, integer : 12 }' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), - '{foo: \'bar\', bar: \'foo: bar\'}' => array('foo' => 'bar', 'bar' => 'foo: bar'), - '{\'foo\': \'bar\', "bar": \'foo: bar\'}' => array('foo' => 'bar', 'bar' => 'foo: bar'), - '{\'foo\'\'\': \'bar\', "bar\"": \'foo: bar\'}' => array('foo\'' => 'bar', "bar\"" => 'foo: bar'), - '{\'foo: \': \'bar\', "bar: ": \'foo: bar\'}' => array('foo: ' => 'bar', "bar: " => 'foo: bar'), - - // nested sequences and mappings - '[foo, [bar, foo]]' => array('foo', array('bar', 'foo')), - '[foo, {bar: foo}]' => array('foo', array('bar' => 'foo')), - '{ foo: {bar: foo} }' => array('foo' => array('bar' => 'foo')), - '{ foo: [bar, foo] }' => array('foo' => array('bar', 'foo')), - - '[ foo, [ bar, foo ] ]' => array('foo', array('bar', 'foo')), - - '[{ foo: {bar: foo} }]' => array(array('foo' => array('bar' => 'foo'))), - - '[foo, [bar, [foo, [bar, foo]], foo]]' => array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo')), - - '[foo, {bar: foo, foo: [foo, {bar: foo}]}, [foo, {bar: foo}]]' => array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo'))), - - '[foo, bar: { foo: bar }]' => array('foo', '1' => array('bar' => array('foo' => 'bar'))), - '[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',), - ); - } - - protected function getTestsForDump() - { - return array( - 'null' => null, - 'false' => false, - 'true' => true, - '12' => 12, - "'quoted string'" => 'quoted string', - '12.30e+02' => 12.30e+02, - '1234' => 0x4D2, - '1243' => 02333, - '.Inf' => -log(0), - '-.Inf' => log(0), - "'686e444'" => '686e444', - '"foo\r\nbar"' => "foo\r\nbar", - "'foo#bar'" => 'foo#bar', - "'foo # bar'" => 'foo # bar', - "'#cfcfcf'" => '#cfcfcf', - - "'a \"string\" with ''quoted strings inside'''" => 'a "string" with \'quoted strings inside\'', - - "'-dash'" => '-dash', - "'-'" => '-', - - // sequences - '[foo, bar, false, null, 12]' => array('foo', 'bar', false, null, 12), - '[\'foo,bar\', \'foo bar\']' => array('foo,bar', 'foo bar'), - - // mappings - '{ foo: bar, bar: foo, \'false\': false, \'null\': null, integer: 12 }' => array('foo' => 'bar', 'bar' => 'foo', 'false' => false, 'null' => null, 'integer' => 12), - '{ foo: bar, bar: \'foo: bar\' }' => array('foo' => 'bar', 'bar' => 'foo: bar'), - - // nested sequences and mappings - '[foo, [bar, foo]]' => array('foo', array('bar', 'foo')), - - '[foo, [bar, [foo, [bar, foo]], foo]]' => array('foo', array('bar', array('foo', array('bar', 'foo')), 'foo')), - - '{ foo: { bar: foo } }' => array('foo' => array('bar' => 'foo')), - - '[foo, { bar: foo }]' => array('foo', array('bar' => 'foo')), - - '[foo, { bar: foo, foo: [foo, { bar: foo }] }, [foo, { bar: foo }]]' => array('foo', array('bar' => 'foo', 'foo' => array('foo', array('bar' => 'foo'))), array('foo', array('bar' => 'foo'))), - - '[foo, \'@foo.baz\', { \'%foo%\': \'foo is %foo%\', bar: \'%foo%\' }, true, \'@service_container\']' => array('foo', '@foo.baz', array('%foo%' => 'foo is %foo%', 'bar' => '%foo%',), true, '@service_container',), - ); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php deleted file mode 100644 index 289965e8d..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParseExceptionTest.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Exception\ParseException; -use Symfony\Component\Yaml\Yaml; - -class ParseExceptionTest extends \PHPUnit_Framework_TestCase -{ - public function testGetMessage() - { - $exception = new ParseException('Error message', 42, 'foo: bar', '/var/www/app/config.yml'); - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - $message = 'Error message in "/var/www/app/config.yml" at line 42 (near "foo: bar")'; - } else { - $message = 'Error message in "\\/var\\/www\\/app\\/config.yml" at line 42 (near "foo: bar")'; - } - - $this->assertEquals($message, $exception->getMessage()); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php deleted file mode 100644 index 07e6222d7..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/ParserTest.php +++ /dev/null @@ -1,619 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Yaml; -use Symfony\Component\Yaml\Parser; - -class ParserTest extends \PHPUnit_Framework_TestCase -{ - protected $parser; - - protected function setUp() - { - $this->parser = new Parser(); - } - - protected function tearDown() - { - $this->parser = null; - } - - /** - * @dataProvider getDataFormSpecifications - */ - public function testSpecifications($file, $expected, $yaml, $comment) - { - if ('escapedCharacters' == $file) { - if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) { - $this->markTestSkipped('The iconv and mbstring extensions are not available.'); - } - } - - $this->assertEquals($expected, var_export($this->parser->parse($yaml), true), $comment); - } - - public function getDataFormSpecifications() - { - $parser = new Parser(); - $path = __DIR__.'/Fixtures'; - - $tests = array(); - $files = $parser->parse(file_get_contents($path.'/index.yml')); - foreach ($files as $file) { - $yamls = file_get_contents($path.'/'.$file.'.yml'); - - // split YAMLs documents - foreach (preg_split('/^---( %YAML\:1\.0)?/m', $yamls) as $yaml) { - if (!$yaml) { - continue; - } - - $test = $parser->parse($yaml); - if (isset($test['todo']) && $test['todo']) { - // TODO - } else { - eval('$expected = '.trim($test['php']).';'); - - $tests[] = array($file, var_export($expected, true), $test['yaml'], $test['test']); - } - } - } - - return $tests; - } - - public function testTabsInYaml() - { - // test tabs in YAML - $yamls = array( - "foo:\n bar", - "foo:\n bar", - "foo:\n bar", - "foo:\n bar", - ); - - foreach ($yamls as $yaml) { - try { - $content = $this->parser->parse($yaml); - - $this->fail('YAML files must not contain tabs'); - } catch (\Exception $e) { - $this->assertInstanceOf('\Exception', $e, 'YAML files must not contain tabs'); - $this->assertEquals('A YAML file cannot contain tabs as indentation at line 2 (near "'.strpbrk($yaml, "\t").'").', $e->getMessage(), 'YAML files must not contain tabs'); - } - } - } - - public function testEndOfTheDocumentMarker() - { - $yaml = <<assertEquals('foo', $this->parser->parse($yaml)); - } - - public function getBlockChompingTests() - { - $tests = array(); - - $yaml = <<<'EOF' -foo: |- - one - two -bar: |- - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |- - one - two - -bar: |- - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |- - one - two -bar: |- - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping strip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two -bar: | - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping clip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two - -bar: | - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping clip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: | - one - two -bar: | - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping clip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two -bar: |+ - one - two - -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo\n", - ); - $tests['Literal block chomping keep with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two - -bar: |+ - one - two - - -EOF; - $expected = array( - 'foo' => "one\ntwo\n\n", - 'bar' => "one\ntwo\n\n", - ); - $tests['Literal block chomping keep with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: |+ - one - two -bar: |+ - one - two -EOF; - $expected = array( - 'foo' => "one\ntwo\n", - 'bar' => "one\ntwo", - ); - $tests['Literal block chomping keep without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two -bar: >- - one - two - -EOF; - $expected = array( - 'foo' => "one two", - 'bar' => "one two", - ); - $tests['Folded block chomping strip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two - -bar: >- - one - two - - -EOF; - $expected = array( - 'foo' => "one two", - 'bar' => "one two", - ); - $tests['Folded block chomping strip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >- - one - two -bar: >- - one - two -EOF; - $expected = array( - 'foo' => "one two", - 'bar' => "one two", - ); - $tests['Folded block chomping strip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two -bar: > - one - two - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping clip with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two - -bar: > - one - two - - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping clip with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: > - one - two -bar: > - one - two -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two", - ); - $tests['Folded block chomping clip without trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two -bar: >+ - one - two - -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two\n", - ); - $tests['Folded block chomping keep with single trailing newline'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two - -bar: >+ - one - two - - -EOF; - $expected = array( - 'foo' => "one two\n\n", - 'bar' => "one two\n\n", - ); - $tests['Folded block chomping keep with multiple trailing newlines'] = array($expected, $yaml); - - $yaml = <<<'EOF' -foo: >+ - one - two -bar: >+ - one - two -EOF; - $expected = array( - 'foo' => "one two\n", - 'bar' => "one two", - ); - $tests['Folded block chomping keep without trailing newline'] = array($expected, $yaml); - - return $tests; - } - - /** - * @dataProvider getBlockChompingTests - */ - public function testBlockChomping($expected, $yaml) - { - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - /** - * Regression test for issue #7989. - * - * @see https://github.com/symfony/symfony/issues/7989 - */ - public function testBlockLiteralWithLeadingNewlines() - { - $yaml = <<<'EOF' -foo: |- - - - bar - -EOF; - $expected = array( - 'foo' => "\n\nbar" - ); - - $this->assertSame($expected, $this->parser->parse($yaml)); - } - - public function testObjectSupportEnabled() - { - $input = <<assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects'); - } - - public function testObjectSupportDisabledButNoExceptions() - { - $input = <<assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects'); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testObjectsSupportDisabledWithExceptions() - { - $this->parser->parse('foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}', true, false); - } - - public function testNonUtf8Exception() - { - if (!function_exists('mb_detect_encoding') || !function_exists('iconv')) { - $this->markTestSkipped('Exceptions for non-utf8 charsets require the mb_detect_encoding() and iconv() functions.'); - - return; - } - - $yamls = array( - iconv("UTF-8", "ISO-8859-1", "foo: 'äöüß'"), - iconv("UTF-8", "ISO-8859-15", "euro: '€'"), - iconv("UTF-8", "CP1252", "cp1252: '©ÉÇáñ'") - ); - - foreach ($yamls as $yaml) { - try { - $this->parser->parse($yaml); - - $this->fail('charsets other than UTF-8 are rejected.'); - } catch (\Exception $e) { - $this->assertInstanceOf('Symfony\Component\Yaml\Exception\ParseException', $e, 'charsets other than UTF-8 are rejected.'); - } - } - } - - /** - * - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - * - */ - public function testUnindentedCollectionException() - { - $yaml = <<parser->parse($yaml); - } - - /** - * @expectedException \Symfony\Component\Yaml\Exception\ParseException - */ - public function testSequenceInAMapping() - { - Yaml::parse(<<assertEquals(array('hash' => null), Yaml::parse($input)); - } - - public function testStringBlockWithComments() - { - $this->assertEquals(array('content' => << -

    title

    - - -footer # comment3 -EOT - ), Yaml::parse(<< -

    title

    - - - footer # comment3 -EOF - )); - } - - public function testFoldedStringBlockWithComments() - { - $this->assertEquals(array(array('content' => << -

    title

    - - -footer # comment3 -EOT - )), Yaml::parse(<< -

    title

    - - - footer # comment3 -EOF - )); - } - - public function testNestedFoldedStringBlockWithComments() - { - $this->assertEquals(array(array( - 'title' => 'some title', - 'content' => << -

    title

    - - -footer # comment3 -EOT - )), Yaml::parse(<< -

    title

    - - - footer # comment3 -EOF - )); - } -} - -class B -{ - public $b = 'foo'; -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php deleted file mode 100644 index 633978d63..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Tests/YamlTest.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml\Tests; - -use Symfony\Component\Yaml\Yaml; - -class YamlTest extends \PHPUnit_Framework_TestCase -{ - public function testParseAndDump() - { - $data = array('lorem' => 'ipsum', 'dolor' => 'sit'); - $yml = Yaml::dump($data); - $parsed = Yaml::parse($yml); - $this->assertEquals($data, $parsed); - - $filename = __DIR__.'/Fixtures/index.yml'; - $contents = file_get_contents($filename); - $parsedByFilename = Yaml::parse($filename); - $parsedByContents = Yaml::parse($contents); - $this->assertEquals($parsedByFilename, $parsedByContents); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php deleted file mode 100644 index 1b8eeed57..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Unescaper.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -/** - * Unescaper encapsulates unescaping rules for single and double-quoted - * YAML strings. - * - * @author Matthew Lewinski - */ -class Unescaper -{ - // Parser and Inline assume UTF-8 encoding, so escaped Unicode characters - // must be converted to that encoding. - const ENCODING = 'UTF-8'; - - // Regex fragment that matches an escaped character in a double quoted - // string. - const REGEX_ESCAPED_CHARACTER = "\\\\([0abt\tnvfre \\\"\\/\\\\N_LP]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})"; - - /** - * Unescapes a single quoted string. - * - * @param string $value A single quoted string. - * - * @return string The unescaped string. - */ - public function unescapeSingleQuotedString($value) - { - return str_replace('\'\'', '\'', $value); - } - - /** - * Unescapes a double quoted string. - * - * @param string $value A double quoted string. - * - * @return string The unescaped string. - */ - public function unescapeDoubleQuotedString($value) - { - $self = $this; - $callback = function ($match) use ($self) { - return $self->unescapeCharacter($match[0]); - }; - - // evaluate the string - return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value); - } - - /** - * Unescapes a character that was found in a double-quoted string - * - * @param string $value An escaped character - * - * @return string The unescaped character - */ - public function unescapeCharacter($value) - { - switch ($value{1}) { - case '0': - return "\x0"; - case 'a': - return "\x7"; - case 'b': - return "\x8"; - case 't': - return "\t"; - case "\t": - return "\t"; - case 'n': - return "\n"; - case 'v': - return "\xb"; - case 'f': - return "\xc"; - case 'r': - return "\xd"; - case 'e': - return "\x1b"; - case ' ': - return ' '; - case '"': - return '"'; - case '/': - return '/'; - case '\\': - return '\\'; - case 'N': - // U+0085 NEXT LINE - return $this->convertEncoding("\x00\x85", self::ENCODING, 'UCS-2BE'); - case '_': - // U+00A0 NO-BREAK SPACE - return $this->convertEncoding("\x00\xA0", self::ENCODING, 'UCS-2BE'); - case 'L': - // U+2028 LINE SEPARATOR - return $this->convertEncoding("\x20\x28", self::ENCODING, 'UCS-2BE'); - case 'P': - // U+2029 PARAGRAPH SEPARATOR - return $this->convertEncoding("\x20\x29", self::ENCODING, 'UCS-2BE'); - case 'x': - $char = pack('n', hexdec(substr($value, 2, 2))); - - return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE'); - case 'u': - $char = pack('n', hexdec(substr($value, 2, 4))); - - return $this->convertEncoding($char, self::ENCODING, 'UCS-2BE'); - case 'U': - $char = pack('N', hexdec(substr($value, 2, 8))); - - return $this->convertEncoding($char, self::ENCODING, 'UCS-4BE'); - } - } - - /** - * Convert a string from one encoding to another. - * - * @param string $value The string to convert - * @param string $to The input encoding - * @param string $from The output encoding - * - * @return string The string with the new encoding - * - * @throws \RuntimeException if no suitable encoding function is found (iconv or mbstring) - */ - private function convertEncoding($value, $to, $from) - { - if (function_exists('mb_convert_encoding')) { - return mb_convert_encoding($value, $to, $from); - } elseif (function_exists('iconv')) { - return iconv($from, $to, $value); - } - - throw new \RuntimeException('No suitable convert encoding function (install the iconv or mbstring extension).'); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php b/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php deleted file mode 100644 index 61793fb3a..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/Yaml.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\Yaml; - -use Symfony\Component\Yaml\Exception\ParseException; - -/** - * Yaml offers convenience methods to load and dump YAML. - * - * @author Fabien Potencier - * - * @api - */ -class Yaml -{ - /** - * Parses YAML into a PHP array. - * - * The parse method, when supplied with a YAML stream (string or file), - * will do its best to convert YAML in a file into a PHP array. - * - * Usage: - * - * $array = Yaml::parse('config.yml'); - * print_r($array); - * - * - * As this method accepts both plain strings and file names as an input, - * you must validate the input before calling this method. Passing a file - * as an input is a deprecated feature and will be removed in 3.0. - * - * @param string $input Path to a YAML file or a string containing YAML - * @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise - * @param bool $objectSupport True if object support is enabled, false otherwise - * - * @return array The YAML converted to a PHP array - * - * @throws ParseException If the YAML is not valid - * - * @api - */ - public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false) - { - // if input is a file, process it - $file = ''; - if (strpos($input, "\n") === false && is_file($input)) { - if (false === is_readable($input)) { - throw new ParseException(sprintf('Unable to parse "%s" as the file is not readable.', $input)); - } - - $file = $input; - $input = file_get_contents($file); - } - - $yaml = new Parser(); - - try { - return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport); - } catch (ParseException $e) { - if ($file) { - $e->setParsedFile($file); - } - - throw $e; - } - } - - /** - * Dumps a PHP array to a YAML string. - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. - * - * @param array $array PHP array - * @param int $inline The level where you switch to inline YAML - * @param int $indent The amount of spaces to use for indentation of nested nodes. - * @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise - * @param bool $objectSupport true if object support is enabled, false otherwise - * - * @return string A YAML string representing the original PHP array - * - * @api - */ - public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) - { - $yaml = new Dumper(); - $yaml->setIndentation($indent); - - return $yaml->dump($array, $inline, 0, $exceptionOnInvalidType, $objectSupport); - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json b/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json deleted file mode 100644 index 33c298535..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/composer.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "symfony/yaml", - "type": "library", - "description": "Symfony Yaml Component", - "keywords": [], - "homepage": "http://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "require": { - "php": ">=5.3.3" - }, - "autoload": { - "psr-0": { "Symfony\\Component\\Yaml\\": "" } - }, - "target-dir": "Symfony/Component/Yaml", - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - } -} diff --git a/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist b/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist deleted file mode 100644 index aa77e9de7..000000000 --- a/core/lib/symfony/yaml/Symfony/Component/Yaml/phpunit.xml.dist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - ./Tests/ - - - - - - ./ - - ./vendor - ./Tests - - - - diff --git a/core/migrations/001-move-source.json b/core/migrations/001-move-source.json deleted file mode 100644 index 08bfd4d28..000000000 --- a/core/migrations/001-move-source.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "core/source", - "destinationPath": "source", - "checkType": "dirEmpty" -} \ No newline at end of file diff --git a/core/migrations/002-check-public-styleguide-exists.json b/core/migrations/002-check-public-styleguide-exists.json deleted file mode 100644 index d6c0392ce..000000000 --- a/core/migrations/002-check-public-styleguide-exists.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "", - "destinationPath": "public/styleguide", - "checkType": "dirExists" -} \ No newline at end of file diff --git a/core/migrations/003-move-styleguide.json b/core/migrations/003-move-styleguide.json deleted file mode 100644 index 9f96dd1ee..000000000 --- a/core/migrations/003-move-styleguide.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "core/styleguide", - "destinationPath": "public/styleguide", - "checkType": "versionDiffDir" -} \ No newline at end of file diff --git a/core/migrations/004-check-meta-exists.json b/core/migrations/004-check-meta-exists.json deleted file mode 100644 index de94846c8..000000000 --- a/core/migrations/004-check-meta-exists.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "", - "destinationPath": "source/_patterns/00-atoms/00-meta", - "checkType": "dirExists" -} \ No newline at end of file diff --git a/core/migrations/005-move-pattern-header-footer.json b/core/migrations/005-move-pattern-header-footer.json deleted file mode 100644 index 31e082f99..000000000 --- a/core/migrations/005-move-pattern-header-footer.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "core/source/_patterns/00-atoms/00-meta", - "destinationPath": "source/_patterns/00-atoms/00-meta", - "checkType": "dirEmpty" -} \ No newline at end of file diff --git a/core/migrations/006-check-public-data-exists.json b/core/migrations/006-check-public-data-exists.json deleted file mode 100644 index cac1ae51c..000000000 --- a/core/migrations/006-check-public-data-exists.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "", - "destinationPath": "public/data", - "checkType": "dirExists" -} \ No newline at end of file diff --git a/core/migrations/007-check-public-patterns-exists.json b/core/migrations/007-check-public-patterns-exists.json deleted file mode 100644 index dcb6c5471..000000000 --- a/core/migrations/007-check-public-patterns-exists.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sourcePath": "", - "destinationPath": "public/patterns", - "checkType": "dirExists" -} \ No newline at end of file diff --git a/core/pageFollowServer.php b/core/pageFollowServer.php deleted file mode 100755 index bd579087d..000000000 --- a/core/pageFollowServer.php +++ /dev/null @@ -1,40 +0,0 @@ -register(); - -// parse the main config for the content sync port -if (!($config = @parse_ini_file(__DIR__."/../config/config.ini"))) { - print "Missing the configuration file. Please build it using the Pattern Lab builder.\n"; - exit; -} - -// give it a default port -$port = ($config) ? trim($config['pageFollowPort']) : '8000'; - -// start the content sync server -$server = new \Wrench\Server('ws://0.0.0.0:'.$port.'/', array()); - -// register the application & run it -$server->registerApplication('pagefollow', new \Wrench\Application\PageFollowApplication()); - -print "\n"; -print "Page Follow Server Started...\n"; -print "Use CTRL+C to stop this service...\n"; - -$server->run(); diff --git a/core/scripts/README b/core/scripts/README deleted file mode 100644 index 95dca5ed7..000000000 --- a/core/scripts/README +++ /dev/null @@ -1,6 +0,0 @@ -These are Mac OS X-compatible files to open up select command line scripts. Simply double-click on a file and it should run -the appropriate shell command. If you receive a permissions error you will need to use the command line to fix it. -Simply go to the command line, cd to the scripts/php directory and type chmod +x [filename]. Replace [filename] with the -appropriate filenames. - -That's confusing. \ No newline at end of file diff --git a/core/scripts/generateSite.command b/core/scripts/generateSite.command deleted file mode 100755 index c339b935a..000000000 --- a/core/scripts/generateSite.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../builder.php" -g \ No newline at end of file diff --git a/core/scripts/generateSiteWithCSS.command b/core/scripts/generateSiteWithCSS.command deleted file mode 100755 index b97af439e..000000000 --- a/core/scripts/generateSiteWithCSS.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../builder.php" -gc \ No newline at end of file diff --git a/core/scripts/startAutoReloadServer.command b/core/scripts/startAutoReloadServer.command deleted file mode 100755 index 934877ba6..000000000 --- a/core/scripts/startAutoReloadServer.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../autoReloadServer.php" \ No newline at end of file diff --git a/core/scripts/startPageFollowServer.command b/core/scripts/startPageFollowServer.command deleted file mode 100755 index e3a1e362f..000000000 --- a/core/scripts/startPageFollowServer.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../pageFollowServer.php" \ No newline at end of file diff --git a/core/scripts/startWatcher.command b/core/scripts/startWatcher.command deleted file mode 100755 index 08c37f182..000000000 --- a/core/scripts/startWatcher.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../builder.php" -w \ No newline at end of file diff --git a/core/scripts/startWatcherWithAutoReload.command b/core/scripts/startWatcherWithAutoReload.command deleted file mode 100755 index 14e251966..000000000 --- a/core/scripts/startWatcherWithAutoReload.command +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -DIR="$( cd "$( dirname "$0" )" && pwd )" -php "$DIR/../builder.php" -wr \ No newline at end of file diff --git a/core/source/_annotations/annotations.js b/core/source/_annotations/annotations.js deleted file mode 100644 index 3919bced6..000000000 --- a/core/source/_annotations/annotations.js +++ /dev/null @@ -1,34 +0,0 @@ -var comments = { -"comments" : [ - { - "el": "header[role=banner]", - "title" : "Masthead", - "comment": "The main header of the site doesn't take up too much screen real estate in order to keep the focus on the core content. It's using a linear CSS gradient instead of a background image to give greater design flexibility and reduce HTTP requests." - }, - { - "el": ".logo", - "title" : "Logo", - "comment": "The logo image is an SVG file, which ensures that the logo displays crisply even on high resolution displays. A PNG fallback is provided for browsers that don't support SVG images.

    Further reading: Optimizing Web Experiences for High Resolution Screens

    " - }, - { - "el": "#nav", - "title" : "Navigation", - "comment": "

    Navigation for adaptive web experiences can be tricky. Top navigations are typical on desktop sites, but mobile screen sizes don't give us the luxury of space. We're dealing with this situation by creating a simple menu anchor that toggles the main navigation on small screens. This is just one method. Bagcheck and Contents Magazine add an anchor in the header that jumps users to the navigation which is placed in the footer. This solution works well because it doesn't require any Javascript in order to work. Other methods exist too. For example, ESPN's mobile navigation overlays the main content of the page.

    The nav is only hidden when a certain level of javascript is supported in order to ensure that users with little/poor javascript support can still access the navigation. Once the screen size is large enough to accommodate the nav, we show the main navigation links and hide the menu anchor.

    See also: Responsive Navigation Patterns

    " - }, - { - "el": ".search-form", - "title" : "Search", - "comment": "

    Search is an incredibly important priority, especially for mobile. It is a great idea to give users the ability to jump directly to what they are looking for without forcing them to wade through your site's navigation. Check out the Burton and Yelp mobile sites for great examples of experiences that prioritize search.

    We're also using the HTML5 search input type, which is great for mobile devices that can bring up the appropriate virtual keyboard for many smartphones. And like the main header navigation, we're hiding the search form on small screens to save space. Clicking the search anchor toggles the form.

    " - }, - { - "el": ".article-header h1", - "title" : "Article Header", - "comment": "

    The article header should be no more than 140 characters.

    " - }, - { - "el": ".block-hero", - "title" : "Hero", - "comment": "

    The hero area highlights one major story using a large image and a captivating headline.

    " - } -] -}; \ No newline at end of file diff --git a/core/source/_data/data.json b/core/source/_data/data.json deleted file mode 100644 index 3be1f4aca..000000000 --- a/core/source/_data/data.json +++ /dev/null @@ -1,93 +0,0 @@ -{ - "title" : "Pattern Lab", - "htmlClass": "pl", - "bodyClass": "body", - "img": { - "landscape-4x3": { - "src": "../../images/fpo_4x3.png", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "../../images/fpo_16x9.png", - "alt": "16x9 Image" - }, - "square": { - "src": "../../images/fpo_square.png", - "alt": "Square Thumbnail" - }, - "avatar" : { - "src" : "../../images/fpo_avatar.png", - "alt" : "Person Name" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/tech", - "alt": "Rectangle" - } - }, - "headline" : { - "short" : "Lorem ipsum dolor sit (37 characters)", - "medium" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit. (72 characters)" - }, - "excerpt" : { - "short" : "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam", - "medium" : "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", - "long" : "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." - }, - "description" : "So, setting about it as methodically as men might smoke out a wasps' nest, the Martians spread this strange stifling vapour over the Londonward country. The horns of the crescent slowly moved apart, until at last they formed a line from Hanwell to Coombe and Malden. All night through their destructive tubes advanced.", - "url" : "#", - "name" : { - "first": "Lacy", - "firsti": "L", - "middle": "Tommie", - "middlei": "T", - "last": "Way", - "lasti": "W" - }, - "year" : { - "long": "2013", - "short": "13" - }, - "month" : { - "long": "February", - "short": "Feb", - "digit": "02" - }, - "dayofweek" : { - "long": "Monday", - "short": "Mon" - }, - "day" : { - "long": "10", - "short": "10", - "ordinal": "th" - }, - "hour" : { - "long": "01", - "short": "1", - "military": "13", - "ampm": "pm" - }, - "minute" : { - "long": "20", - "short": "20" - }, - "seconds" : "31", - "author" : { - "first-name": "Author", - "last-name": "Name" - }, - "hero": true, - "emergency" : false, - "touts" : [ - { }, - { }, - { } - ], - "latest-posts" : [ - { }, - { }, - { }, - { }, - { } - ] -} \ No newline at end of file diff --git a/core/source/_data/listitems.json b/core/source/_data/listitems.json deleted file mode 100644 index 114ed1f9b..000000000 --- a/core/source/_data/listitems.json +++ /dev/null @@ -1,878 +0,0 @@ -{ - "1": { - "title": "Nullizzle shizznit velizzle, hizzle, suscipit own yo', gravida vizzle, arcu.", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/people", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/nature", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/tech", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/tech", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/tech", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Lorizzle pimpin' dolizzle sit amet I", - "medium": "Rizzle adipiscing elizzle. Nullam sapien velizzle, shit volutpizzle, my" - }, - "excerpt": { - "short": "Shizz fo shizzle mah nizzle fo rizzle, mah home g-dizzle, gravida vizzle, arcu. Pellentesque crunk tortizzle. Sed erizzle. Black izzle sheezy telliv.", - "medium": "Izzle crazy tempizzle sizzle. We gonna chung gangsta get down get down fo shizzle turpizzle. Away break it down black. Pellentesque bling bling rhoncus fo shizzle. In hac the bizzle platea dictumst. Black dapibizzle. Crackalackin.", - "long": "Curabitizzle fo shizzle diam quizzle nisi nizzle mollizzle. Suspendisse boofron. Morbi odio. Sure pizzle. Crazy orci. Shut the shizzle up maurizzle get down get down, check out this a, go to hizzle sit amizzle, malesuada izzle, pede. Pellentesque gravida. Vestibulizzle check it out mi, volutpat izzle, shiz sed, shiznit sempizzle, da bomb. Funky fresh in ipsum. Da bomb volutpat felis vizzle daahng dawg. Crizzle quis dope izzle fo shizzle my ni." - }, - "description": "Fizzle crazy tortor. Sed rizzle. Ass pimpin' dolor dapibizzle turpis tempizzle fo shizzle my nizzle. Maurizzle pellentesque its fo rizzle izzle turpis. Get down get down we gonna chung nizzle. Shizzlin dizzle eleifend rhoncizzle break it down. In yo ghetto platea dictumst. Bling bling dapibizzle. Curabitur break yo neck, yall fo, pretizzle eu, go to hizzle dope, own yo' vitae, nunc. Bizzle suscipizzle. Ass semper velit sizzle fo.", - "url": "http://lorizzle.nl/", - "name": { - "first": "Junius", - "firsti": "J", - "middle": "Marius", - "middlei": "M", - "last": "Koolen", - "lasti": "K" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "January", - "short": "Jan", - "digit": "01" - }, - "dayofweek": { - "long": "Sunday", - "short": "Sun" - }, - "day": { - "long": "01", - "short": "1", - "ordinal": "st" - }, - "hour": { - "long": "06", - "short": "6", - "military": "06", - "ampm": "am" - }, - "minute": { - "long": "20", - "short": "20" - }, - "seconds": "31" - }, - "2": { - "title": "Veggies sunt bona vobis, proinde vos postulo", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/nature", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/tech", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/people", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/people", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/people", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Veggies sunt bona vobis, proinde vos", - "medium": "Postulo esse magis azuki bean burdock brussels sprout quandong komatsun" - }, - "excerpt": { - "short": "A fava bean collard greens endive tomatillo lotus root okra winter purslane zucchini parsley spinach artichoke. Brussels sprout pea turnip catsear.", - "medium": "Bush tomato gumbo potato garbanzo ricebean burdock daikon coriander kale quandong. Bok choy celery leek avocado shallot horseradish aubergine parsley. Bok choy bell pepper kale celery desert raisin kakadu plum bok choy bunya nuts.", - "long": "Spinach tigernut. Corn cucumber grape black-eyed pea asparagus spinach avocado dulse bunya nuts epazote celery desert raisin celtuce burdock plantain yarrow napa cabbage. Plantain okra seakale endive tigernut pea sprouts asparagus corn chard peanut beet greens groundnut radicchio carrot coriander gumbo gram celtuce. Jícama nori bamboo shoot collard greens okra radicchio tomato. Catsear mustard corn tigernut celery kale water spinach bok choy." - }, - "description": "Mung bean squash sorrel taro coriander collard greens gumbo bitterleaf tomato. Taro water chestnut celtuce turnip yarrow celery endive scallion black-eyed pea onion. Aubergine dulse turnip greens mustard salsify garlic soybean parsley bitterleaf desert raisin courgette.", - "url": "http://veggieipsum.com", - "name": { - "first": "Siguror", - "firsti": "S", - "middle": "Aron", - "middlei": "A", - "last": "Hanigan", - "lasti": "H" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "February", - "short": "Feb", - "digit": "02" - }, - "dayofweek": { - "long": "Monday", - "short": "Mon" - }, - "day": { - "long": "10", - "short": "10", - "ordinal": "th" - }, - "hour": { - "long": "01", - "short": "1", - "military": "13", - "ampm": "pm" - }, - "minute": { - "long": "20", - "short": "20" - }, - "seconds": "31" - }, - "3": { - "title": "Bacon ipsum dolor sit amet turducken strip steak beef ribs shank", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/tech", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/people", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/nature", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/nature", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/nature", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Bacon ipsum dolor sit amet spare rib", - "medium": "Tongue pancetta short ribs bacon. Kielbasa ball tip cow bresaola, capic" - }, - "excerpt": { - "short": "Tail jerky rump shoulder t-bone meatball meatloaf salami. Filet mignon shank t-bone venison, ham hock ribeye drumstick bresaola kielbasa. Frankfurter.", - "medium": "Doner biltong turducken leberkas. Rump swine pork loin ribeye ball tip meatloaf, pork chop ground round pig pancetta cow biltong brisket. Beef corned beef beef ribs, bacon pork belly sausage meatball boudin doner ham hock. Swine gro.", - "long": "Und round meatball, bacon pig leberkas corned beef tongue shoulder. Drumstick pork loin prosciutto ball tip shank pancetta spare ribs jowl pastrami. Frankfurter boudin filet mignon ribeye. Pig hamburger strip steak ham turducken prosciutto bresaola ground round pancetta frankfurter jowl. Frankfurter tongue brisket tenderloin, beef ribs pastrami biltong tail bresaola flank. Biltong pork chop beef boudin hamburger bacon. Capicola bresaola sausage." - }, - "description": "Boudin sausage jerky pastrami ground round salami biltong. Sausage fatback strip steak doner pork loin, pork belly drumstick ham short loin hamburger shankle. Short ribs sirloin rump tri-tip beef biltong. Meatball pig salami, jowl pork loin fatback short loin drumstick andouille.", - "url": "http://baconipsum.com/", - "name": { - "first": "Teun", - "firsti": "T", - "middle": "Jodocus", - "middlei": "J", - "last": "Richard", - "lasti": "R" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "March", - "short": "Mar", - "digit": "03" - }, - "dayofweek": { - "long": "Tuesday", - "short": "Tue" - }, - "day": { - "long": "22", - "short": "22", - "ordinal": "nd" - }, - "hour": { - "long": "04", - "short": "4", - "military": "16", - "ampm": "pm" - }, - "minute": { - "long": "45", - "short": "45" - }, - "seconds": "11" - }, - "4": { - "title": "Whatever swag accusamus occupy, gentrify butcher tote bag", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/animals", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/arch", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/people/grayscale", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/people/greyscale", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/people/greyscale", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Nesciunt sunt cillum keytar Pitchfork", - "medium": "Tote bag mixtape PBR Helvetica scenester forage four loko. Irure Tonx" - }, - "excerpt": { - "short": "Golf quis +1, Wes Anderson church-key lo-fi keffiyeh selvage culpa authentic Brooklyn fap chambray. Id synth yr, 3 wolf moon locavore +1 mixtape do.", - "medium": "Sed single-origin coffee anim eu. Bicycle rights Neutra Truffaut pop-up. Paleo hella irure meh Banksy, Wes Anderson typewriter VHS jean shorts yr. Eiusmod officia banjo Thundercats, odio laborum magna deep v cornhole nostrud kitsch.", - "long": "Tattooed Williamsburg. Jean shorts proident kogi laboris. Non tote bag pariatur elit slow-carb, Vice irure eu Echo Park ea aliqua chillwave. Cornhole Etsy quinoa Pinterest cardigan. Excepteur quis forage, Blue Bottle keffiyeh velit hoodie direct trade typewriter Etsy. Fingerstache squid non, sriracha drinking vinegar Shoreditch pork belly. Paleo sartorial mollit 3 wolf moon chambray whatever, sed tote bag small batch freegan. Master cleanse." - }, - "description": "Fanny pack ullamco et veniam semiotics. Shoreditch PBR reprehenderit cliche, magna Tonx aesthetic. Narwhal photo booth DIY aute post-ironic anim. Vice cliche brunch est before they sold out fap, street art Odd Future fashion axe messenger bag nihil Tonx tattooed. Nihil hashtag incididunt, do eu art party Banksy jean shorts four loko typewriter.", - "url": "http://hipsteripsum.me/", - "name": { - "first": "Duane", - "firsti": "D", - "middle": "Edvin", - "middlei": "E", - "last": "Wilms", - "lasti": "W" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "April", - "short": "Apr", - "digit": "04" - }, - "dayofweek": { - "long": "Wednesday", - "short": "Wed" - }, - "day": { - "long": "13", - "short": "13", - "ordinal": "th" - }, - "hour": { - "long": "10", - "short": "10", - "military": "10", - "ampm": "am" - }, - "minute": { - "long": "14", - "short": "14" - }, - "seconds": "52" - }, - "5": { - "title": "Marshall McLuhan Colbert bump backpack journalist vast wasteland Romenesko CPM", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/people/grayscale", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/animals", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/arch", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/arch", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/arch", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Blog meme masthead DocumentCloud Fou", - "medium": "Square tabloid Andy Carvin stupid commenters, Nick Denton mathewi semip" - }, - "excerpt": { - "short": "I love the Weather & Opera section Groupon copyright in the slot, Journal Register open newsroom analytics future totally blowing up on Twitter AOL.", - "medium": "CTR mthomps Flipboard do what you do best and link to the rest Buttry media bias Journal Register RT, newspaper strike do what you do best and link to the rest semipermeable learnings cognitive surplus mathewi, Encyclo Google News.", - "long": "Pulse mathewi Project Thunderdome digital first. HuffPo social media optimization try PR dying the notion of the public monetization data visualization audience atomization overcome community, libel lawyer twitterati should isn't a business model fair use innovation Facebook AOL, Walter Cronkite died for your sins horse-race coverage crowdfunding Patch but what's the business model rubber cement horse-race coverage. Lucius Nieman content farm." - }, - "description": "Like button audience atomization overcome Colbert bump Free Darko inverted pyramid we will make them pay, digital circulation strategy Like button totally blowing up on Twitter church of the savvy. Pictures of Goats section open source discuss Frontline analog thinking filters paidContent.", - "url": "http://www.niemanlab.org/journo-ipsum/", - "name": { - "first": "Frans", - "firsti": "F", - "middle": "Fabius", - "middlei": "F", - "last": "Keegan", - "lasti": "K" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "May", - "short": "May", - "digit": "05" - }, - "dayofweek": { - "long": "Thursday", - "short": "Thu" - }, - "day": { - "long": "26", - "short": "26", - "ordinal": "th" - }, - "hour": { - "long": "06", - "short": "6", - "military": "18", - "ampm": "pm" - }, - "minute": { - "long": "37", - "short": "37" - }, - "seconds": "24" - }, - "6": { - "title": "Thunder, thunder, thundercats, Ho!", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/arch", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/animals", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/people/grayscale", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/people/grayscale", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/people/grayscale", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Hong Kong Phooey, number one super g", - "medium": "Hong Kong Phooey, quicker than the human eye. He's got style, a groovy" - }, - "excerpt": { - "short": "Style, and a car that just won't stop. When the going gets tough, he's really rough, with a Hong Kong Phooey chop (Hi-Ya!). Hong Kong Phooey, number.", - "medium": "One super guy. Hong Kong Phooey, quicker than the human eye. Hong Kong Phooey, he's fan-riffic! One for all and all for one, Muskehounds are always ready. One for all and all for one, helping everybody. One for all and all for one.", - "long": "It's a pretty story. Sharing everything with fun, that's the way to be. One for all and all for one, Muskehounds are always ready. One for all and all for one, helping everybody. One for all and all for one, can sound pretty corny. If you've got a problem chum, think how it could be. This is my boss, Jonathan Hart, a self-made millionaire, he's quite a guy. This is Mrs H., she's gorgeous, she's one lady who knows how to take care of herself." - }, - "description": "Beats all you've ever saw, been in trouble with the law since the day they was born. Straight'nin' the curve, flat'nin' the hills. Someday the mountain might get 'em, but the law never will. Makin' their way, the only way they know how, that's just a little bit more than the law will allow. Just good ol' boys, wouldn't change if they could, fightin' the system like a true modern day Robin Hood.", - "url": "http://www.malevole.com/mv/misc/text/", - "name": { - "first": "Fergus", - "firsti": "F", - "middle": "Jon", - "middlei": "J", - "last": "Althuis", - "lasti": "A" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "June", - "short": "Jun", - "digit": "06" - }, - "dayofweek": { - "long": "Friday", - "short": "Fri" - }, - "day": { - "long": "08", - "short": "8", - "ordinal": "th" - }, - "hour": { - "long": "11", - "short": "11", - "military": "23", - "ampm": "pm" - }, - "minute": { - "long": "37", - "short": "37" - }, - "seconds": "33" - }, - "7": { - "title": "Yeah, I like animals better than people sometimes", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/any", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/any", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/any", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/any", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/any", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Now that we know who you are, I know", - "medium": "Who I am. I'm not a mistake! It all makes sense! In a comic, you know" - }, - "excerpt": { - "short": "How you can tell who the arch-villain's going to be? He's the exact opposite of the hero. And most times they're friends, like you and me! I should've known way back when... You know why, David? Because of the kids. They called me.", - "medium": "The lysine contingency - it's intended to prevent the spread of the animals is case they ever got off the island. Dr. Wu inserted a gene that makes a single faulty enzyme in protein metabolism. The animals can't manufacture the amin.", - "long": "Do you see any Teletubbies in here? Do you see a slender plastic tag clipped to my shirt with my name printed on it? Do you see a little Asian child with a blank expression on his face sitting outside on a mechanical helicopter that shakes when you put quarters in it? No? Well, that's what you see at a toy store. And you must think you're in a toy store, because you're here shopping for an infant named Jeb. Acid lysine. Unless they're." - }, - "description": "Especially dogs. Dogs are the best. Every time you come home, they act like they haven't seen you in a year. And the good thing about dogs... is they got different dogs for different people. Like pit bulls. The dog of dogs. Pit bull can be the right man's best friend... or the wrong man's worst enemy. You going to give me a dog for a pet, give me a pit bull.", - "url": "http://slipsum.com/lite/", - "name": { - "first": "Bertil", - "firsti": "B", - "middle": "Pier", - "middlei": "P", - "last": "Aaij", - "lasti": "A" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "July", - "short": "Jul", - "digit": "07" - }, - "dayofweek": { - "long": "Saturday", - "short": "Sat" - }, - "day": { - "long": "22", - "short": "22", - "ordinal": "nd" - }, - "hour": { - "long": "11", - "short": "11", - "military": "11", - "ampm": "am" - }, - "minute": { - "long": "12", - "short": "12" - }, - "seconds": "47" - }, - "8": { - "title": "Webtwo ipsum dolor sit amet, eskobo chumby doostang bebo", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/any/grayscale", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/any/grayscale", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/any/grayscale", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/any/grayscale", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/any/grayscale", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Webtwo ipsum dolor sit amet, eskobo", - "medium": "Chumby doostang bebo. Wakoopa oooj geni zoho loopt eskobo sifteo chart" - }, - "excerpt": { - "short": "Dropio, chumby waze dopplr plugg oooj yammer jibjab imvu yuntaa knewton, mobly trulia airbnb bitly chegg tivo empressr knewton. Plickers spock voxy.", - "medium": "Zooomr kippt voxy zinch appjet napster trulia, zappos wufoo zapier spotify mzinga jaiku fleck, disqus lijit voxy voki yoono. Dogster elgg jibjab xobni kazaa bebo udemy sifteo kiko, elgg knewton skype mog octopart zoodles kazaa udem.", - "long": "Appjet spock handango empressr lijit palantir weebly dropio jibjab revver kaboodle spotify orkut mobly chegg akismet, handango ebay woopra revver joukuu kosmix unigo oooooc wufoo zanga kno zinch spock knewton. Balihoo greplin bebo squidoo skype kaboodle meebo disqus joost gooru, zlio tumblr edmodo palantir eskobo shopify kiko gsnap. Greplin balihoo chartly plugg imeem diigo trulia plickers qeyno wikia akismet, palantir grockit prezi jabber zo." - }, - "description": "Wufoo diigo grockit sifteo divvyshot, unigo zooomr revver. Edmodo appjet joyent skype bubbli jajah zoodles joukuu xobni hojoki edmodo appjet, mozy mzinga akismet yuntaa joost yuntaa geni tivo insala yoono chumby, grockit sococo loopt zanga etsy cloudera koofers empressr jiglu blippy. Omgpop lanyrd joukuu sococo zimbra airbnb movity jibjab, foodzie.", - "url": "http://web20ipsum.com", - "name": { - "first": "Freyr", - "firsti": "F", - "middle": "Ninian", - "middlei": "N", - "last": "Hines", - "lasti": "H" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "August", - "short": "Aug", - "digit": "08" - }, - "dayofweek": { - "long": "Sunday", - "short": "Sun" - }, - "day": { - "long": "31", - "short": "31", - "ordinal": "st" - }, - "hour": { - "long": "03", - "short": "3", - "military": "15", - "ampm": "pm" - }, - "minute": { - "long": "42", - "short": "42" - }, - "seconds": "21" - }, - "9": { - "title": "Rebel Mission to Ord Mantell", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/any/sepia", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/any/sepia", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/any/sepia", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/any/sepia", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/any/sepia", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "All right. Well, take care of yourself, Han", - "medium": "You don't believe in the Force, do you? The Force is strong with this one" - }, - "excerpt": { - "short": "I'm trying not to, kid. I find your lack of faith disturbing. You are a part of the Rebel Alliance and a traitor! Take her away! I want to come with.", - "medium": "I'm surprised you had the courage to take the responsibility yourself. Don't be too proud of this technological terror you've constructed. The ability to destroy a planet is insignificant next to the power of the Force. You don't be.", - "long": "A tremor in the Force. The last time I felt it was in the presence of my old master. You don't believe in the Force, do you? I have traced the Rebel spies to her. Now she is my only link to finding their secret base. A tremor in the Force. The last time I felt it was in the presence of my old master. I'm trying not to, kid. The more you tighten your grip, Tarkin, the more star systems will slip through your fingers. There's nothing for me here." - }, - "description": "I find your lack of faith disturbing. A tremor in the Force. The last time I felt it was in the presence of my old master. Don't act so surprised, Your Highness. You weren't on any mercy mission this time. Several transmissions were beamed to this ship by Rebel spies. I want to know what happened to the plans they sent you. The plans you refer to will soon be back in our hands.", - "url": "http://chrisvalleskey.com/fillerama/", - "name": { - "first": "Jacobus", - "firsti": "J", - "middle": "Domitianus", - "middlei": "D", - "last": "Sneiders", - "lasti": "S" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "September", - "short": "Sep", - "digit": "09" - }, - "dayofweek": { - "long": "Monday", - "short": "Mon" - }, - "day": { - "long": "04", - "short": "4", - "ordinal": "th" - }, - "hour": { - "long": "09", - "short": "9", - "military": "09", - "ampm": "am" - }, - "minute": { - "long": "04", - "short": "4" - }, - "seconds": "37" - }, - "10": { - "title": "Help, help, I'm being repressed!", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/tech/grayscale", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/nature/grayscale", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/arch/grayscale", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/arch/grayscale", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/arch/grayscale", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "The swallow may fly south with the sun", - "medium": "On second thoughts, let's not go there. It is a silly place. You don't" - }, - "excerpt": { - "short": "The swallow may fly south with the sun, and the house martin or the plover may seek warmer climes in winter, yet these are not strangers to our land.", - "medium": "The Knights Who Say Ni demand a sacrifice! Found them? In Mercia?! The coconut's tropical! Where'd you get the coconuts? Why do you think that she is a witch? I am your king. You don't vote for kings. But you are dressed as one. Oh, ow!", - "long": "Well, I didn't vote for you. Burn her! Be quiet! He hasn't got shit all over him. Where'd you get the coconuts? The swallow may fly south with the sun, and the house martin or the plover may seek warmer climes in winter, yet these are not strangers to our land. No! Yes, yes. A bit. But she's got a wart. Shut up! Will you shut up?! I have to push the pram a lot. Now, look here, my good man. Frighten us, English pig-dogs! Go and boil your bottoms." - }, - "description": "The Knights Who Say Ni demand a sacrifice! …Are you suggesting that coconuts migrate? Knights of Ni, we are but simple travelers who seek the enchanter who lives beyond these woods. You don't frighten us, English pig-dogs! Go and boil your bottoms, sons of a silly person! I blow my nose at you, so-called Ah-thoor Keeng, you and all your silly English K-n-n-n-n-n-n-n-niggits!", - "url": "http://chrisvalleskey.com/fillerama/", - "name": { - "first": "Plinius", - "firsti": "P", - "middle": "Varinius", - "middlei": "V", - "last": "Sloane", - "lasti": "S" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "October", - "short": "Oct", - "digit": "10" - }, - "dayofweek": { - "long": "Tuesday", - "short": "Tue" - }, - "day": { - "long": "25", - "short": "25", - "ordinal": "th" - }, - "hour": { - "long": "03", - "short": "3", - "military": "03", - "ampm": "am" - }, - "minute": { - "long": "51", - "short": "51" - }, - "second": "19" - }, - "11": { - "title": "Danish danish candy canes bonbon cheesecake danish marzipan", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/tech/sepia", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/animals/sepia", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/arch/sepia", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/arch/sepia", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/arch/sepia", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Carrot cake fruitcake dessert apple", - "medium": "Pie powder lemon drops sesame snaps cake brownie. Biscuit ice cream gin" - }, - "excerpt": { - "short": "Bread cotton candy marzipan. Baker too go gingerbread topping cupcake donut. Fruitcake marzipan bear claw tart toffee candy cheesecake. Lemon drops.", - "medium": "Cupcake chupa chups pudding gummies. Unerdwear.com cupcake candy soufflé sesame snaps macaroon sesame snaps. Tart dragée muffin. Sweet roll gummi bears caramels fruitcake candy cake. Cotton candy carrot cake tart cotton candy. Jelly.", - "long": "Gingerbread candy icing pastry cake bonbon fruitcake donut. Powder liquorice dessert tart croissant cake. Dessert chocolate cake sweet roll candy candy sesame snaps tiramisu ice cream. Candy candy canes marzipan biscuit cupcake pie pudding. Donut cotton candy muffin. Pastry bear claw icing halvah. Gingerbread cotton candy sweet roll toffee chocolate jujubes. Wafer jujubes danish ice cream lemon drops wafer. Sesame snaps cupcake gummies browni." - }, - "description": "Sugar plum wafer soufflé ice cream. Wafer topping biscuit pie gummi bears topping. Gummies toffee powder applicake oat cake cookie. Bear claw candy tootsie roll fruitcake danish applicake candy canes macaroon. Liquorice tiramisu danish cotton candy gummies. Tiramisu dessert gummi bears macaroon sweet roll jelly-o gummi bears marzipan.", - "url": "http://cupcakeipsum.com/", - "name": { - "first": "Matthias", - "firsti": "M", - "middle": "Brady", - "middlei": "B", - "last": "Macguinness", - "lasti": "M" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "November", - "short": "Nov", - "digit": "11" - }, - "dayofweek": { - "long": "Wednesday", - "short": "Wed" - }, - "day": { - "long": "19", - "short": "19", - "ordinal": "th" - }, - "hour": { - "long": "11", - "short": "11", - "military": "23", - "ampm": "pm" - }, - "minute": { - "long": "55", - "short": "55" - }, - "seconds": "12" - }, - "12": { - "title": "Cottage cheese brie lancashire. Boursin when the cheese comes out.", - "img": { - "avatar": { - "src": "http://placeimg.com/100/100/people/sepia", - "alt": "Avatar" - }, - "square": { - "src": "http://placeimg.com/300/300/people/sepia", - "alt": "Square" - }, - "rectangle": { - "src": "http://placeimg.com/400/300/people/sepia", - "alt": "Rectangle" - }, - "landscape-4x3": { - "src": "http://placeimg.com/400/300/people/sepia", - "alt": "4x3 Image" - }, - "landscape-16x9": { - "src": "http://placeimg.com/640/360/people/sepia", - "alt": "16x9 Image" - } - }, - "headline": { - "short": "Cauliflower cheese cream cheese baby", - "medium": "Lancashire cheesy feet rubber cheese cheese and wine gouda the big chee" - }, - "excerpt": { - "short": "Queso fromage. Taleggio boursin bavarian bergkase cream cheese when the cheese comes out everybody's happy port-salut halloumi pecorino. Caerphilly cut the cheese manchego camembert de normandie goat melted cheese cheese and biscuit.", - "medium": "Pecorino queso lancashire. Manchego lancashire cheesy feet emmental babybel cheese strings dolcelatte bavarian bergkase. Ricotta cheese slices cheesy grin cow cheesecake smelly cheese mascarpone lancashire. Cow say cheese babybel do.", - "long": "Cheesy grin macaroni cheese airedale. Fromage frais airedale cheese and wine brie cow swiss swiss mozzarella. Emmental cheese triangles edam rubber cheese pepper jack ricotta airedale airedale. Brie parmesan smelly cheese cheese strings stinking bishop cheese strings taleggio. Bocconcini blue castello gouda. Everyone loves caerphilly rubber cheese halloumi smelly cheese melted cheese melted cheese bavarian bergkase. Rubber cheese ricotta emm." - }, - "description": "Queso caerphilly cheesecake. Parmesan chalk and cheese port-salut port-salut babybel cottage cheese cheesy grin pepper jack. Croque monsieur paneer st. agur blue cheese emmental airedale monterey jack bavarian bergkase cheese triangles. Halloumi parmesan.", - "url": "http://www.cheeseipsum.co.uk/", - "name": { - "first": "Aquila", - "firsti": "A", - "middle": "Gaius", - "middlei": "G", - "last": "Achterkamp", - "lasti": "A" - }, - "year": { - "long": "2013", - "short": "13" - }, - "month": { - "long": "December", - "short": "Dec", - "digit": "12" - }, - "dayofweek": { - "long": "Thursday", - "short": "Thu" - }, - "day": { - "long": "28", - "short": "28", - "ordinal": "th" - }, - "hour": { - "long": "08", - "short": "8", - "military": "08", - "ampm": "am" - }, - "minute": { - "long": "34", - "short": "34" - }, - "seconds": "56" - } -} \ No newline at end of file diff --git a/core/source/_meta/_00-head.mustache b/core/source/_meta/_00-head.mustache deleted file mode 100644 index 938c0d2db..000000000 --- a/core/source/_meta/_00-head.mustache +++ /dev/null @@ -1,16 +0,0 @@ - - - - {{ title }} - - - - - - - {% pattern-lab-head %} - - - - - \ No newline at end of file diff --git a/core/source/_meta/_01-foot.mustache b/core/source/_meta/_01-foot.mustache deleted file mode 100644 index 5bd39a972..000000000 --- a/core/source/_meta/_01-foot.mustache +++ /dev/null @@ -1,6 +0,0 @@ - - - {% pattern-lab-foot %} - - - \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/01-global/00-colors.mustache b/core/source/_patterns/00-atoms/01-global/00-colors.mustache deleted file mode 100644 index 7f2653f8d..000000000 --- a/core/source/_patterns/00-atoms/01-global/00-colors.mustache +++ /dev/null @@ -1,38 +0,0 @@ -
      -
    • - - #ff0000 -
    • -
    • - - #00ff00 -
    • -
    • - - #0000ff -
    • -
    • - - #ffff00 -
    • -
    • - - #00ffff -
    • -
    • - - #ff00ff -
    • -
    • - - #ffffff -
    • -
    • - - #808080 -
    • -
    • - - #000000 -
    • -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/01-global/01-fonts.mustache b/core/source/_patterns/00-atoms/01-global/01-fonts.mustache deleted file mode 100644 index a292ff438..000000000 --- a/core/source/_patterns/00-atoms/01-global/01-fonts.mustache +++ /dev/null @@ -1,6 +0,0 @@ -

    Primary font: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

    -

    Primary font italic: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

    -

    Primary font bold: "HelveticaNeue", "Helvetica", "Arial", sans-serif;

    -

    Secondary font: Georgia, Times, "Times New Roman", serif;

    -

    Secondary font italic: Georgia, Times, "Times New Roman", serif;

    -

    Secondary font bold; Georgia, Times, "Times New Roman", serif;

    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/01-global/02-animations.mustache b/core/source/_patterns/00-atoms/01-global/02-animations.mustache deleted file mode 100644 index 7d3074575..000000000 --- a/core/source/_patterns/00-atoms/01-global/02-animations.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
    Fade: Duration: 0.3s Easing: ease-out (Hover to see effect)
    - -
    Movement: Duration: 0.8s Easing: ease-in-out; (Hover to see effect)
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/01-global/03-visibility.mustache b/core/source/_patterns/00-atoms/01-global/03-visibility.mustache deleted file mode 100644 index 13e602122..000000000 --- a/core/source/_patterns/00-atoms/01-global/03-visibility.mustache +++ /dev/null @@ -1,11 +0,0 @@ -

    This text is hidden on smaller screens

    - -

    This text is only visible on smaller screens

    - -

    This text is hidden on medium screens only

    - -

    This text is only visible on medium screens

    - -

    This text is hidden on large screens

    - -

    This text is only visible on large screens

    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/00-headings.mustache b/core/source/_patterns/00-atoms/02-text/00-headings.mustache deleted file mode 100644 index 43f648c7f..000000000 --- a/core/source/_patterns/00-atoms/02-text/00-headings.mustache +++ /dev/null @@ -1,6 +0,0 @@ -

    Heading Level 1

    -

    Heading Level 2

    -

    Heading Level 3

    -

    Heading Level 4

    -
    Heading Level 5
    -
    Heading Level 6
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/01-paragraph.mustache b/core/source/_patterns/00-atoms/02-text/01-paragraph.mustache deleted file mode 100644 index 4ed3f6c01..000000000 --- a/core/source/_patterns/00-atoms/02-text/01-paragraph.mustache +++ /dev/null @@ -1 +0,0 @@ -

    A paragraph (from the Greek paragraphos, "to write beside" or "written beside") is a self-contained unit of a discourse in writing dealing with a particular point or idea. A paragraph consists of one or more sentences. Though not required by the syntax of any language, paragraphs are usually an expected part of formal writing, used to organize longer prose.

    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/02-blockquote.mustache b/core/source/_patterns/00-atoms/02-text/02-blockquote.mustache deleted file mode 100644 index 2f11319b3..000000000 --- a/core/source/_patterns/00-atoms/02-text/02-blockquote.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/03-inline-elements.mustache b/core/source/_patterns/00-atoms/02-text/03-inline-elements.mustache deleted file mode 100644 index dd4ff1f39..000000000 --- a/core/source/_patterns/00-atoms/02-text/03-inline-elements.mustache +++ /dev/null @@ -1,41 +0,0 @@ -
    -

    This is a text link

    - -

    Strong is used to indicate strong importance

    - -

    This text has added emphasis

    - -

    The b element is stylistically different text from normal text, without any special importance

    - -

    The i element is text that is set off from the normal text

    - -

    The u element is text with an unarticulated, though explicitly rendered, non-textual annotation

    - -

    This text is deleted and This text is inserted

    - -

    This text has a strikethrough

    - -

    Superscript®

    - -

    Subscript for things like H2O

    - -

    This small text is small for for fine print, etc.

    - -

    Abbreviation: HTML

    - -

    Keybord input: Cmd

    - -

    This text is a short inline quotation

    - -

    This is a citation - -

    The dfn element indicates a definition.

    - -

    The mark element indicates a highlight

    - -

    This is what inline code looks like.

    - -

    This is sample output from a computer program

    - -

    The variarble element, such as x = y

    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/04-time.mustache b/core/source/_patterns/00-atoms/02-text/04-time.mustache deleted file mode 100644 index 26cecc136..000000000 --- a/core/source/_patterns/00-atoms/02-text/04-time.mustache +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/05-preformatted-text.mustache b/core/source/_patterns/00-atoms/02-text/05-preformatted-text.mustache deleted file mode 100644 index 70de25e11..000000000 --- a/core/source/_patterns/00-atoms/02-text/05-preformatted-text.mustache +++ /dev/null @@ -1,9 +0,0 @@ -
      	
    -P R E F O R M A T T E D T E X T
    -! " # $ % & ' ( ) * + , - . /
    -0 1 2 3 4 5 6 7 8 9 : ; < = > ?
    -@ A B C D E F G H I J K L M N O
    -P Q R S T U V W X Y Z [ \ ] ^ _
    -` a b c d e f g h i j k l m n o
    -p q r s t u v w x y z { | } ~ 
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/02-text/06-hr.mustache b/core/source/_patterns/00-atoms/02-text/06-hr.mustache deleted file mode 100644 index 1d6667d2e..000000000 --- a/core/source/_patterns/00-atoms/02-text/06-hr.mustache +++ /dev/null @@ -1 +0,0 @@ -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/03-lists/00-unordered.mustache b/core/source/_patterns/00-atoms/03-lists/00-unordered.mustache deleted file mode 100644 index 3f9de349f..000000000 --- a/core/source/_patterns/00-atoms/03-lists/00-unordered.mustache +++ /dev/null @@ -1,14 +0,0 @@ -
    -
      -
    • This is a list item in an unordered list
    • -
    • An unordered list is a list in which the sequence of items is not important. Sometimes, an unordered list is a bulleted list. And this is a long list item in an unordered list that can wrap onto a new line.
    • -
    • - Lists can be nested inside of each other -
        -
      • This is a nested list item
      • -
      • This is another nested list item in an unordered list
      • -
      -
    • -
    • This is the last list item
    • -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/03-lists/01-ordered.mustache b/core/source/_patterns/00-atoms/03-lists/01-ordered.mustache deleted file mode 100644 index e206c7b10..000000000 --- a/core/source/_patterns/00-atoms/03-lists/01-ordered.mustache +++ /dev/null @@ -1,14 +0,0 @@ -
    -
      -
    1. This is a list item in an ordered list
    2. -
    3. An ordered list is a list in which the sequence of items is important. An ordered list does not necessarily contain sequence characters.
    4. -
    5. - Lists can be nested inside of each other -
        -
      1. This is a nested list item
      2. -
      3. This is another nested list item in an ordered list
      4. -
      -
    6. -
    7. This is the last list item
    8. -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/03-lists/02-definition.mustache b/core/source/_patterns/00-atoms/03-lists/02-definition.mustache deleted file mode 100644 index 0488e0812..000000000 --- a/core/source/_patterns/00-atoms/03-lists/02-definition.mustache +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    Definition List
    -
    A number of connected items or names written or printed consecutively, typically one below the other.
    -
    This is a term.
    -
    This is the definition of that term, which both live in a dl.
    -
    Here is another term.
    -
    And it gets a definition too, which is this line.
    -
    Here is term that shares a definition with the term below.
    -
    And it gets a definition too, which is this line.
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/00-logo.mustache b/core/source/_patterns/00-atoms/04-images/00-logo.mustache deleted file mode 100644 index 8fa9d34c3..000000000 --- a/core/source/_patterns/00-atoms/04-images/00-logo.mustache +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/01-landscape-4x3.mustache b/core/source/_patterns/00-atoms/04-images/01-landscape-4x3.mustache deleted file mode 100644 index cf874ee9d..000000000 --- a/core/source/_patterns/00-atoms/04-images/01-landscape-4x3.mustache +++ /dev/null @@ -1 +0,0 @@ -{{ img.landscape-4x3.alt }} \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/02-landscape-16x9.mustache b/core/source/_patterns/00-atoms/04-images/02-landscape-16x9.mustache deleted file mode 100644 index 9efc5eeb3..000000000 --- a/core/source/_patterns/00-atoms/04-images/02-landscape-16x9.mustache +++ /dev/null @@ -1 +0,0 @@ -{{ img.landscape-16x9.alt }} \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/03-square.mustache b/core/source/_patterns/00-atoms/04-images/03-square.mustache deleted file mode 100644 index 805cce5f2..000000000 --- a/core/source/_patterns/00-atoms/04-images/03-square.mustache +++ /dev/null @@ -1 +0,0 @@ -{{ img.square.alt }} \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/04-avatar.mustache b/core/source/_patterns/00-atoms/04-images/04-avatar.mustache deleted file mode 100644 index 6b79ad0c5..000000000 --- a/core/source/_patterns/00-atoms/04-images/04-avatar.mustache +++ /dev/null @@ -1 +0,0 @@ -Avatar \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/05-icons.mustache b/core/source/_patterns/00-atoms/04-images/05-icons.mustache deleted file mode 100644 index 4e2512348..000000000 --- a/core/source/_patterns/00-atoms/04-images/05-icons.mustache +++ /dev/null @@ -1,12 +0,0 @@ -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • - -
    • -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/06-loading-icon.mustache b/core/source/_patterns/00-atoms/04-images/06-loading-icon.mustache deleted file mode 100644 index 9a10165ab..000000000 --- a/core/source/_patterns/00-atoms/04-images/06-loading-icon.mustache +++ /dev/null @@ -1 +0,0 @@ -Loading \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/04-images/07-favicon.mustache b/core/source/_patterns/00-atoms/04-images/07-favicon.mustache deleted file mode 100644 index 07e985d37..000000000 --- a/core/source/_patterns/00-atoms/04-images/07-favicon.mustache +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/05-forms/00-text-fields.mustache b/core/source/_patterns/00-atoms/05-forms/00-text-fields.mustache deleted file mode 100644 index 90cbb7747..000000000 --- a/core/source/_patterns/00-atoms/05-forms/00-text-fields.mustache +++ /dev/null @@ -1,38 +0,0 @@ -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/05-forms/01-select-menu.mustache b/core/source/_patterns/00-atoms/05-forms/01-select-menu.mustache deleted file mode 100644 index 760c9199a..000000000 --- a/core/source/_patterns/00-atoms/05-forms/01-select-menu.mustache +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    - - -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/05-forms/02-checkbox.mustache b/core/source/_patterns/00-atoms/05-forms/02-checkbox.mustache deleted file mode 100644 index 017a8dc5c..000000000 --- a/core/source/_patterns/00-atoms/05-forms/02-checkbox.mustache +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    - Checkbox * -
      -
    • -
    • -
    • -
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/05-forms/03-radio-buttons.mustache b/core/source/_patterns/00-atoms/05-forms/03-radio-buttons.mustache deleted file mode 100644 index 68c83eaeb..000000000 --- a/core/source/_patterns/00-atoms/05-forms/03-radio-buttons.mustache +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    - Radio -
      -
    • -
    • -
    • -
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/05-forms/04-html5-inputs.mustache b/core/source/_patterns/00-atoms/05-forms/04-html5-inputs.mustache deleted file mode 100644 index 99c22cce7..000000000 --- a/core/source/_patterns/00-atoms/05-forms/04-html5-inputs.mustache +++ /dev/null @@ -1,10 +0,0 @@ -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/06-buttons/00-buttons.mustache b/core/source/_patterns/00-atoms/06-buttons/00-buttons.mustache deleted file mode 100644 index bdbeec360..000000000 --- a/core/source/_patterns/00-atoms/06-buttons/00-buttons.mustache +++ /dev/null @@ -1,4 +0,0 @@ -

    Button

    -

    Alternate Button

    -

    Disabled Button

    -

    Text Button

    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/07-tables/00-table.mustache b/core/source/_patterns/00-atoms/07-tables/00-table.mustache deleted file mode 100644 index 53153d519..000000000 --- a/core/source/_patterns/00-atoms/07-tables/00-table.mustache +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Table Heading 1Table Heading 2Table Heading 3Table Heading 4Table Heading 5
    Table Footer 1Table Footer 2Table Footer 3Table Footer 4Table Footer 5
    Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
    Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
    Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
    Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
    \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/08-media/_00-video.mustache b/core/source/_patterns/00-atoms/08-media/_00-video.mustache deleted file mode 100644 index f28fcd7fc..000000000 --- a/core/source/_patterns/00-atoms/08-media/_00-video.mustache +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/00-atoms/08-media/_01-audio.mustache b/core/source/_patterns/00-atoms/08-media/_01-audio.mustache deleted file mode 100644 index 91c82135e..000000000 --- a/core/source/_patterns/00-atoms/08-media/_01-audio.mustache +++ /dev/null @@ -1,4 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/00-text/00-byline.mustache b/core/source/_patterns/01-molecules/00-text/00-byline.mustache deleted file mode 100644 index 08366cabe..000000000 --- a/core/source/_patterns/01-molecules/00-text/00-byline.mustache +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/00-text/01-address.mustache b/core/source/_patterns/01-molecules/00-text/01-address.mustache deleted file mode 100644 index a13ea79fc..000000000 --- a/core/source/_patterns/01-molecules/00-text/01-address.mustache +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    Company Name
    -
    -
    1234 Main St.
    - Anywhere, - 101010, - CA -
    U.S.A
    -
    -
    +1.888.123.4567
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/00-text/02-heading-group.mustache b/core/source/_patterns/01-molecules/00-text/02-heading-group.mustache deleted file mode 100644 index bf18f5ac4..000000000 --- a/core/source/_patterns/01-molecules/00-text/02-heading-group.mustache +++ /dev/null @@ -1,4 +0,0 @@ -
    -

    This is the heading group's main heading

    -

    This is the heading group's subheading

    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/00-text/03-blockquote-with-citation.mustache b/core/source/_patterns/01-molecules/00-text/03-blockquote-with-citation.mustache deleted file mode 100644 index b9b6c98fc..000000000 --- a/core/source/_patterns/01-molecules/00-text/03-blockquote-with-citation.mustache +++ /dev/null @@ -1,4 +0,0 @@ -
    -

    A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

    - Quote Source -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/00-text/04-intro-text.mustache b/core/source/_patterns/01-molecules/00-text/04-intro-text.mustache deleted file mode 100644 index b2b1bdef8..000000000 --- a/core/source/_patterns/01-molecules/00-text/04-intro-text.mustache +++ /dev/null @@ -1 +0,0 @@ -

    The intro text may be a lead-in to the passage of text, or it may just be used to create a visual distinction between the rest of the passage of text.

    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/01-layout/00-one-up.mustache b/core/source/_patterns/01-molecules/01-layout/00-one-up.mustache deleted file mode 100644 index eb58a0e24..000000000 --- a/core/source/_patterns/01-molecules/01-layout/00-one-up.mustache +++ /dev/null @@ -1,7 +0,0 @@ -
    - -
    -
    1/1
    -
    - -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/01-layout/01-two-up.mustache b/core/source/_patterns/01-molecules/01-layout/01-two-up.mustache deleted file mode 100644 index ff1553aee..000000000 --- a/core/source/_patterns/01-molecules/01-layout/01-two-up.mustache +++ /dev/null @@ -1,7 +0,0 @@ -
    - -
    -
    1/2
    -
    1/2
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/01-layout/02-three-up.mustache b/core/source/_patterns/01-molecules/01-layout/02-three-up.mustache deleted file mode 100644 index d68fb4854..000000000 --- a/core/source/_patterns/01-molecules/01-layout/02-three-up.mustache +++ /dev/null @@ -1,8 +0,0 @@ -
    - -
    -
    1/3
    -
    1/3
    -
    1/3
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/01-layout/03-four-up.mustache b/core/source/_patterns/01-molecules/01-layout/03-four-up.mustache deleted file mode 100644 index f7351c023..000000000 --- a/core/source/_patterns/01-molecules/01-layout/03-four-up.mustache +++ /dev/null @@ -1,9 +0,0 @@ -
    - -
    -
    1/4
    -
    1/4
    -
    1/4
    -
    1/4
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/00-media-block.mustache b/core/source/_patterns/01-molecules/02-blocks/00-media-block.mustache deleted file mode 100644 index 4bb4e162e..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/00-media-block.mustache +++ /dev/null @@ -1,11 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/01-block-headline-byline.mustache b/core/source/_patterns/01-molecules/02-blocks/01-block-headline-byline.mustache deleted file mode 100644 index df763126c..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/01-block-headline-byline.mustache +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/02-block-hero.mustache b/core/source/_patterns/01-molecules/02-blocks/02-block-hero.mustache deleted file mode 100644 index 39aa02a34..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/02-block-hero.mustache +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/03-block-thumb-headline.mustache b/core/source/_patterns/01-molecules/02-blocks/03-block-thumb-headline.mustache deleted file mode 100644 index 1ef071877..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/03-block-thumb-headline.mustache +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/04-block-headline.mustache b/core/source/_patterns/01-molecules/02-blocks/04-block-headline.mustache deleted file mode 100644 index 5a32707f3..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/04-block-headline.mustache +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/02-blocks/05-block-inset.mustache b/core/source/_patterns/01-molecules/02-blocks/05-block-inset.mustache deleted file mode 100644 index 45817ccf8..000000000 --- a/core/source/_patterns/01-molecules/02-blocks/05-block-inset.mustache +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/03-media/00-figure-with-caption.mustache b/core/source/_patterns/01-molecules/03-media/00-figure-with-caption.mustache deleted file mode 100644 index 98e2b4435..000000000 --- a/core/source/_patterns/01-molecules/03-media/00-figure-with-caption.mustache +++ /dev/null @@ -1,4 +0,0 @@ -
    - {{> atoms-landscape-4x3 }} -
    This is an example of an image with a caption. Photo captions, also known as cutlines, are a few lines of text used to explain or elaborate on published photographs.
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/04-forms/00-search.mustache b/core/source/_patterns/01-molecules/04-forms/00-search.mustache deleted file mode 100644 index 604b6125f..000000000 --- a/core/source/_patterns/01-molecules/04-forms/00-search.mustache +++ /dev/null @@ -1,11 +0,0 @@ -
    -
    - Search - - - -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/04-forms/01-comment-form.mustache b/core/source/_patterns/01-molecules/04-forms/01-comment-form.mustache deleted file mode 100644 index e4360727b..000000000 --- a/core/source/_patterns/01-molecules/04-forms/01-comment-form.mustache +++ /dev/null @@ -1,20 +0,0 @@ -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/04-forms/02-newsletter.mustache b/core/source/_patterns/01-molecules/04-forms/02-newsletter.mustache deleted file mode 100644 index 041e2319e..000000000 --- a/core/source/_patterns/01-molecules/04-forms/02-newsletter.mustache +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/05-navigation/00-primary-nav.mustache b/core/source/_patterns/01-molecules/05-navigation/00-primary-nav.mustache deleted file mode 100644 index 84942ffb7..000000000 --- a/core/source/_patterns/01-molecules/05-navigation/00-primary-nav.mustache +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/core/source/_patterns/01-molecules/05-navigation/01-footer-nav.mustache b/core/source/_patterns/01-molecules/05-navigation/01-footer-nav.mustache deleted file mode 100644 index d46a36b0c..000000000 --- a/core/source/_patterns/01-molecules/05-navigation/01-footer-nav.mustache +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/05-navigation/02-breadcrumbs.mustache b/core/source/_patterns/01-molecules/05-navigation/02-breadcrumbs.mustache deleted file mode 100644 index 3b5c15351..000000000 --- a/core/source/_patterns/01-molecules/05-navigation/02-breadcrumbs.mustache +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/05-navigation/03-pagination.mustache b/core/source/_patterns/01-molecules/05-navigation/03-pagination.mustache deleted file mode 100644 index 8b6de64a3..000000000 --- a/core/source/_patterns/01-molecules/05-navigation/03-pagination.mustache +++ /dev/null @@ -1,9 +0,0 @@ -
      -
    1. 1
    2. -
    3. 2
    4. -
    5. 3
    6. -
    7. 4
    8. -
    9. 5
    10. -
    11. 6
    12. -
    13. 7
    14. -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/05-navigation/04-tabs.mustache b/core/source/_patterns/01-molecules/05-navigation/04-tabs.mustache deleted file mode 100644 index 8e4ec10e2..000000000 --- a/core/source/_patterns/01-molecules/05-navigation/04-tabs.mustache +++ /dev/null @@ -1,7 +0,0 @@ -
    - -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/06-components/00-social-share.mustache b/core/source/_patterns/01-molecules/06-components/00-social-share.mustache deleted file mode 100644 index 120527d78..000000000 --- a/core/source/_patterns/01-molecules/06-components/00-social-share.mustache +++ /dev/null @@ -1,8 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/06-components/01-accordion.mustache b/core/source/_patterns/01-molecules/06-components/01-accordion.mustache deleted file mode 100644 index 253962dcb..000000000 --- a/core/source/_patterns/01-molecules/06-components/01-accordion.mustache +++ /dev/null @@ -1,19 +0,0 @@ -
    - -
    \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/06-components/02-single-comment.mustache b/core/source/_patterns/01-molecules/06-components/02-single-comment.mustache deleted file mode 100644 index 094229a86..000000000 --- a/core/source/_patterns/01-molecules/06-components/02-single-comment.mustache +++ /dev/null @@ -1,9 +0,0 @@ -
  • -
    - {{> atoms-avatar }} -

    {{ name.first }} {{ name.last }}

    -
    -
    -

    {{ description }}

    -
    -
  • \ No newline at end of file diff --git a/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache b/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache deleted file mode 100644 index 971484885..000000000 --- a/core/source/_patterns/01-molecules/07-messaging/00-alert.mustache +++ /dev/null @@ -1,3 +0,0 @@ -
    - {{ excerpt.short }} -
    \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/00-global/00-header.mustache b/core/source/_patterns/02-organisms/00-global/00-header.mustache deleted file mode 100644 index e4a73fb88..000000000 --- a/core/source/_patterns/02-organisms/00-global/00-header.mustache +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/00-global/01-footer.mustache b/core/source/_patterns/02-organisms/00-global/01-footer.mustache deleted file mode 100644 index 63f6fa5ef..000000000 --- a/core/source/_patterns/02-organisms/00-global/01-footer.mustache +++ /dev/null @@ -1,7 +0,0 @@ - -
    -
    - -
    -
    - \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/01-article/00-article-body.mustache b/core/source/_patterns/02-organisms/01-article/00-article-body.mustache deleted file mode 100644 index 9d9b27806..000000000 --- a/core/source/_patterns/02-organisms/01-article/00-article-body.mustache +++ /dev/null @@ -1,22 +0,0 @@ -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer fringilla sem a urna porttitor fringilla. Nulla eget justo felis.

    - -

    Aliquam erat volutpat. Mauris vulputate scelerisque feugiat. Cras a erat a diam venenatis aliquam. Sed tempus, purus ac pretium varius, risus orci sagittis purus, quis auctor libero magna nec magna. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas eros dolor, rutrum eu sollicitudin eu, commodo at leo. Suspendisse potenti. Sed eu nibh sit amet quam auctor feugiat vel et risus. Maecenas eu urna adipiscing neque dictum mollis suscipit in augue. Praesent pulvinar condimentum sagittis. Maecenas laoreet neque non eros consectetur fringilla. Donec vitae risus leo, vitae pharetra ipsum. Sed placerat eros eget elit iaculis semper. Aliquam congue blandit orci ac pretium.

    - -{{> atoms-landscape-16x9 }} - -

    Aliquam ultrices cursus mauris, eget volutpat justo mattis nec. Sed a orci turpis. Aliquam aliquet placerat dui, consectetur tincidunt leo tristique et. Vivamus enim nisi, blandit a venenatis quis, convallis et arcu. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris libero sapien, placerat in sodales eu, tempor quis dui. Vivamus egestas faucibus pulvinar. Maecenas eget diam nunc. Phasellus at sem eros, ac suscipit neque. Phasellus sollicitudin libero a odio dignissim scelerisque. Aliquam purus nulla, tempor eget ullamcorper quis, rhoncus non dui. -

    - -{{> atoms-blockquote }} - -

    Cras at fringilla ipsum. Donec nec libero eget est blandit dignissim a eu ante. Morbi augue nulla, luctus eu sagittis vel, malesuada ut felis. Aliquam erat volutpat. Morbi malesuada augue ac massa hendrerit fermentum. Integer scelerisque lacus a dolor convallis lobortis. Curabitur mollis ante in massa ultricies dignissim. -

    - -{{> atoms-unordered }} - -{{> atoms-ordered }} - -

    Donec posuere fringilla nunc, vitae venenatis diam scelerisque vel. Nullam vitae mauris magna. Mauris et diam quis justo volutpat tincidunt congue nec magna. Curabitur vitae orci elit. Ut mollis massa id magna vestibulum consequat. Proin rutrum lectus justo, sit amet tincidunt est. Vivamus vitae lacinia risus. -

    - -

    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reiciendis, suscipit repellendus nulla accusantium deserunt sed explicabo voluptate sapiente ratione inventore molestiae nihil earum repellat quia odit vitae perspiciatis aliquam amet?

    \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/02-comments/00-comment-thread.mustache b/core/source/_patterns/02-organisms/02-comments/00-comment-thread.mustache deleted file mode 100644 index 4901f2bb2..000000000 --- a/core/source/_patterns/02-organisms/02-comments/00-comment-thread.mustache +++ /dev/null @@ -1,12 +0,0 @@ -
    -
    -

    59 Comments

    - {{> molecules-comment-form }} -
      - {{# listItems.five }} - {{> molecules-single-comment }} - {{/ listItems.five }} -
    -
    - {{> molecules-pagination }} -
    \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache b/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache deleted file mode 100644 index 55b1cf401..000000000 --- a/core/source/_patterns/02-organisms/03-sections/00-latest-posts.mustache +++ /dev/null @@ -1,9 +0,0 @@ -
    -

    Latest Posts

    -
      - {{# latest-posts}} -
    • {{> molecules-media-block }}
    • - {{/ latest-posts}} -
    - View more posts -
    \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/03-sections/01-recent-tweets.mustache b/core/source/_patterns/02-organisms/03-sections/01-recent-tweets.mustache deleted file mode 100644 index edd6d568d..000000000 --- a/core/source/_patterns/02-organisms/03-sections/01-recent-tweets.mustache +++ /dev/null @@ -1,12 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/02-organisms/03-sections/02-related-posts.mustache b/core/source/_patterns/02-organisms/03-sections/02-related-posts.mustache deleted file mode 100644 index b4a75cbed..000000000 --- a/core/source/_patterns/02-organisms/03-sections/02-related-posts.mustache +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/core/source/_patterns/03-templates/00-homepage.mustache b/core/source/_patterns/03-templates/00-homepage.mustache deleted file mode 100644 index 8d26c6ba0..000000000 --- a/core/source/_patterns/03-templates/00-homepage.mustache +++ /dev/null @@ -1,40 +0,0 @@ -
    - {{> organisms-header }} -
    - {{# emergency }} - {{> molecules-alert:error }} - {{/ emergency }} - {{# hero }} - {{> molecules-block-hero }} - {{/ hero}} - -
    - {{# touts}} -
    - {{> molecules-block-inset }} -
    - {{/ touts}} -
    - -
    - -
    -
    -
    -

    Latest Posts

    -
      - {{# latest-posts }} -
    • {{> molecules-media-block }}
    • - {{/ latest-posts }} -
    - View more posts -
    -
    - - -
    -
    - {{> organisms-footer }} -
    \ No newline at end of file diff --git a/core/source/_patterns/03-templates/01-blog.mustache b/core/source/_patterns/03-templates/01-blog.mustache deleted file mode 100644 index b55a9ba23..000000000 --- a/core/source/_patterns/03-templates/01-blog.mustache +++ /dev/null @@ -1,17 +0,0 @@ -
    - {{> organisms-header }} -
    -

    Our Outdoor Blog

    -
    -
    - {{> organisms-latest-posts }} - {{> molecules-pagination }} -
    - - -
    -
    - {{> organisms-footer }} -
    \ No newline at end of file diff --git a/core/source/_patterns/03-templates/02-article.mustache b/core/source/_patterns/03-templates/02-article.mustache deleted file mode 100644 index 5886df59a..000000000 --- a/core/source/_patterns/03-templates/02-article.mustache +++ /dev/null @@ -1,24 +0,0 @@ -
    - {{> organisms-header }} -
    -
    -
    -
    -
    -

    Article Headline Lorem ipsum dolor sit aweofij

    - {{> molecules-byline }} -
    - {{> organisms-article-body }} -
    - {{> molecules-social-share }} - {{> organisms-comment-thread }} -
    - - -
    -
    - {{> organisms-footer }} -
    \ No newline at end of file diff --git a/core/source/_patterns/04-pages/00-homepage.json b/core/source/_patterns/04-pages/00-homepage.json deleted file mode 100644 index 186cf82ea..000000000 --- a/core/source/_patterns/04-pages/00-homepage.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "title" : "Home Page", - "bodyClass": "home", - "emergency" : false, - "hero" : [ - { - "img": { - "landscape-16x9": { - "src": "../../images/sample/landscape-16x9-mountains.jpg", - "alt": "Mountains" - } - }, - "headline" : { - "medium" : "Top 10 mountain ranges for hiking" - } - } - ], - "touts" : [ - { - "img": { - "landscape-4x3": { - "src": "../../images/sample/tout-4x3-climber.jpg", - "alt": "Climber" - } - }, - "headline" : { - "short" : "Extreme climbing tips" - } - }, - { - "img": { - "landscape-4x3": { - "src": "../../images/sample/tout-4x3-stream.jpg", - "alt": "Stream hiking" - } - }, - "headline" : { - "short" : "Stream hiking" - } - }, - { - "img": { - "landscape-4x3": { - "src": "../../images/sample/tout-4x3-climbers.jpg", - "alt": "Explore hiking trails" - } - }, - "headline" : { - "short" : "Explore hiking trails" - } - } - ], - "latest-posts" : [ - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-river.jpg", - "alt": "Post Thumbnail" - } - }, - "headline" : { - "short" : "Navigate the Allegheny River" - }, - "excerpt" : { - "medium" : "The Allegheny River is a principal tributary of the Ohio River; it is located in the Eastern United States. The Allegheny River joins with the Monongahela River to form the Ohio River at the Point of Point State Park in Downtown Pittsburgh, Pennsylvania." - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-ivy.jpg", - "alt": "Ivy" - } - }, - "headline" : { - "short" : "How to detect and avoid poison ivy" - }, - "excerpt" : { - "medium" : "Toxicodendron radicans, commonly known as poison ivy is a poisonous North American plant that is well known for its production of urushiol" - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-yosemite.jpg", - "alt": "Yosemite" - } - }, - "headline" : { - "short" : "Top 10 hiking mountains" - }, - "excerpt" : { - "medium" : "Yosemite National Park is a United States National Park spanning eastern portions of Tuolumne, Mariposa and Madera counties in the central eastern portion of the U.S. state of California." - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-fire.jpg", - "alt": "Fire" - } - }, - "headline" : { - "short" : "How to build a campfire" - }, - "excerpt" : { - "medium" : "A campfire is a fire lit at a campsite, to serve the following functions: light, warmth, a beacon, an insect and/or apex predator deterrent, to cook, and for a psychological sense of security. " - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-gear.jpg", - "alt": "Camping Gear" - } - }, - "headline" : { - "short" : "Pick the right camping gear" - }, - "excerpt" : { - "medium" : "The equipment used in camping varies with the particular type of camping. For instance, in survival camping the equipment consists of small items which have the purpose of helping the camper in providing food, heat and safety." - } - } - ], - "inset-blocks" : [ - { - "headline" : "This is the headline for the inset block" - } - ], - "latest-block" : [ - { - "headline" : "This is the headline for the latest block" - } - ] -} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/00-homepage.mustache b/core/source/_patterns/04-pages/00-homepage.mustache deleted file mode 100644 index 68a930daa..000000000 --- a/core/source/_patterns/04-pages/00-homepage.mustache +++ /dev/null @@ -1 +0,0 @@ -{{> templates-homepage }} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/00-homepage~emergency.json b/core/source/_patterns/04-pages/00-homepage~emergency.json deleted file mode 100644 index 2167094b8..000000000 --- a/core/source/_patterns/04-pages/00-homepage~emergency.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "emergency": { - "alertClass" : "error", - "excerpt" : { - "short" : "Emergency! This is a variation of the core homepage template." - } - } -} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/01-blog.json b/core/source/_patterns/04-pages/01-blog.json deleted file mode 100644 index e0d8ed504..000000000 --- a/core/source/_patterns/04-pages/01-blog.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "title" : "Blog", - "bodyClass": "blog", - "latest-posts" : [ - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-river.jpg", - "alt": "Post Thumbnail" - } - }, - "headline" : { - "short" : "Navigate the Allegheny River" - }, - "excerpt" : { - "medium" : "The Allegheny River is a principal tributary of the Ohio River; it is located in the Eastern United States. The Allegheny River joins with the Monongahela River to form the Ohio River at the Point of Point State Park in Downtown Pittsburgh, Pennsylvania." - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-ivy.jpg", - "alt": "Ivy" - } - }, - "headline" : { - "short" : "How to detect and avoid poison ivy" - }, - "excerpt" : { - "medium" : "Toxicodendron radicans, commonly known as poison ivy is a poisonous North American plant that is well known for its production of urushiol" - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-yosemite.jpg", - "alt": "Yosemite" - } - }, - "headline" : { - "short" : "Top 10 hiking mountains" - }, - "excerpt" : { - "medium" : "Yosemite National Park is a United States National Park spanning eastern portions of Tuolumne, Mariposa and Madera counties in the central eastern portion of the U.S. state of California." - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-fire.jpg", - "alt": "Fire" - } - }, - "headline" : { - "short" : "How to build a campfire" - }, - "excerpt" : { - "medium" : "A campfire is a fire lit at a campsite, to serve the following functions: light, warmth, a beacon, an insect and/or apex predator deterrent, to cook, and for a psychological sense of security. " - } - }, - { - "img": { - "square": { - "src": "../../images/sample/thumb-square-gear.jpg", - "alt": "Camping Gear" - } - }, - "headline" : { - "short" : "Pick the right camping gear" - }, - "excerpt" : { - "medium" : "The equipment used in camping varies with the particular type of camping. For instance, in survival camping the equipment consists of small items which have the purpose of helping the camper in providing food, heat and safety." - } - } - ], - "inset-blocks" : [ - { - "headline" : "This is the headline for the inset block" - } - ], - "latest-block" : [ - { - "headline" : "This is the headline for the latest block" - } - ] -} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/01-blog.mustache b/core/source/_patterns/04-pages/01-blog.mustache deleted file mode 100644 index bd1218023..000000000 --- a/core/source/_patterns/04-pages/01-blog.mustache +++ /dev/null @@ -1 +0,0 @@ -{{> templates-blog }} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/02-article.json b/core/source/_patterns/04-pages/02-article.json deleted file mode 100644 index 86c4c95d2..000000000 --- a/core/source/_patterns/04-pages/02-article.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "title" : "Top 10 Trails You Have To Hike Before You Die", - "bodyClass": "article", - "author" : { - "first-name" : "Julianne", - "last-name" : "McCormick" - }, - "img": { - "landscape-4x3": { - "src": "../../images/sample/landscape-16x9-mountains.jpg", - "alt": "Mountains" - }, - "landscape-16x9": { - "src": "../../images/sample/landscape-16x9-mountains.jpg", - "alt": "Mountains" - } - }, - "inset-blocks" : [ - { - "headline" : "This is the headline for the inset block" - } - ], - "latest-block" : [ - { - "headline" : "This is the headline for the latest block" - } - ] -} \ No newline at end of file diff --git a/core/source/_patterns/04-pages/02-article.mustache b/core/source/_patterns/04-pages/02-article.mustache deleted file mode 100644 index 0449d165c..000000000 --- a/core/source/_patterns/04-pages/02-article.mustache +++ /dev/null @@ -1 +0,0 @@ -{{> templates-article }} \ No newline at end of file diff --git a/core/source/css/scss/base/_animation.scss b/core/source/css/scss/base/_animation.scss deleted file mode 100644 index d28bc9136..000000000 --- a/core/source/css/scss/base/_animation.scss +++ /dev/null @@ -1,13 +0,0 @@ -.animate-fade { - @include transition(opacity, 0.3s, ease-out); - - &:hover { - opacity: 0; - } -} - -.animate-move { - > .demo-shape { - @include transition(all, 0.8s, ease-in-out); - } -} \ No newline at end of file diff --git a/core/source/css/scss/base/_forms.scss b/core/source/css/scss/base/_forms.scss deleted file mode 100644 index 1c02c6a83..000000000 --- a/core/source/css/scss/base/_forms.scss +++ /dev/null @@ -1,111 +0,0 @@ -/*------------------------------------*\ - $FORMS -\*------------------------------------*/ -form ol, form ul { - list-style: none; - margin-left: 0; -} - -fieldset { - border: 0; - padding: 0; - margin: 0; -} - -label { - display: block; - padding-bottom: $space-half; -} - -button, input, select, textarea { - font-family: inherit; - font-size: 100%; - margin: 0 1px 0; -} - -input, textarea { - width: 100%; - border: 1px solid $gray; - padding: $pad-half 0.65rem; -} - -input[type=text], input[type=search], input[type=url], input[type=number], textarea { - -webkit-appearance: none; -} - -button, input[type="submit"] { - padding: $pad-half; - background: $gray-dark; - border: 1px solid $gray; - cursor: pointer; -} - -input[type="checkbox"], -input[type="radio"] { - width: auto; - margin-right: 0.3em; -} - -input[type="search"] { - -webkit-appearance: none; - border-radius: 0; -} - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -//Form Field Container -.field-container { - margin-bottom: $space; -} - -.inline-form { - fieldset, .inline-container { - position: relative; - } - - input[type=submit], button, .btn { - font-size: $font-size-small-2; - padding: 0.65rem 1.3rem; - background: $gray-dark; - position: absolute; - top: 0; - right: 0; - z-index: 1; - width: auto; - - &:hover, &:focus { - background: $gray; - color: $white; - } - } -} - -/* Validation */ -.has-error { - border-color: $error; -} -.is-valid { - border-color: $valid; -} - - - - -/*------------------------------------*\ - $SPECIFIC FORMS -\*------------------------------------*/ - -/* Search Form */ -.search-field { - padding-right: 3em; -} -.inline-form .search-submit { - background: none; - padding: 0.78em 1em; - border: 0; - border-left: 1px solid $gray; - color: $gray; -} diff --git a/core/source/css/scss/base/_global-classes.scss b/core/source/css/scss/base/_global-classes.scss deleted file mode 100644 index 2e13f8858..000000000 --- a/core/source/css/scss/base/_global-classes.scss +++ /dev/null @@ -1,104 +0,0 @@ -/*------------------------------------*\ - $GLOBAL CLASSES -\*------------------------------------*/ - -/* Clearfix */ -.cf { - *zoom: 1; -} -.cf:before, .cf:after { - content: " "; /* 1 */ - display: table; /* 2 */ -} - -.cf:after { - clear: both; -} - -/* Completely remove from the flow and screen readers. */ -.is-hidden { - display: none !important; - visibility: hidden !important; -} - -/* Completely remove from the flow but leave available to screen readers. */ -.is-vishidden { - position: absolute !important; - overflow: hidden; - width: 1px; - height: 1px; - padding: 0; - border: 0; - clip: rect(1px, 1px, 1px, 1px); -} - -/* Floats */ -.right { - float: right; - padding: 0 0 $pad $pad; -} - -.right-search { - float: right; - padding: 0 0 $pad 0; -} - -.left { - float: left; - padding: 0 $pad $pad 0; -} - -/* Text-Align */ -.align-right { - text-align: right; -} - -.align-center { - text-align: center; -} - -.align-left { - text-align: left; -} - -/* Display Classes */ -.hide-small { - @media all and (max-width: $bp-med) { - display: none; - } -} - -.hide-med { - @media all and (min-width: $bp-med) and (max-width: $bp-large) { - display: none; - } -} - -.hide-large { - @media all and (min-width: $bp-large) { - display: none; - } -} - -//States -.valid { - color: $valid; -} - -.error { - color: $error; -} - -.warning { - color: $warning; -} - -.information { - color: $information; -} - -.font-secondary { - font-family: $font-secondary; -} - - diff --git a/core/source/css/scss/base/_headings.scss b/core/source/css/scss/base/_headings.scss deleted file mode 100644 index 5f2433645..000000000 --- a/core/source/css/scss/base/_headings.scss +++ /dev/null @@ -1,31 +0,0 @@ -/* Headings */ -/*Further Reading: http:/csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css/ */ -h1, .alpha { - line-height: 1.2; -} - -h2, .beta { - line-height: 1.2; -} - -h3, .gamma { - line-height: 1.2; -} - -h4, .delta { - -} - -h5, .epsilon { - -} - -h6, .zeta { - -} - -/* Subheadings */ -.subheading { - font-family: $font-secondary; - font-weight: normal; -} diff --git a/core/source/css/scss/base/_links.scss b/core/source/css/scss/base/_links.scss deleted file mode 100644 index d70545d76..000000000 --- a/core/source/css/scss/base/_links.scss +++ /dev/null @@ -1,10 +0,0 @@ -/* Links */ -a { - color: $gray-dark; - text-decoration: none; - outline: 0; - - &:hover, &:focus { - color: $gray; - } -} \ No newline at end of file diff --git a/core/source/css/scss/base/_lists.scss b/core/source/css/scss/base/_lists.scss deleted file mode 100644 index 4ceb7cf5c..000000000 --- a/core/source/css/scss/base/_lists.scss +++ /dev/null @@ -1,19 +0,0 @@ -ol, ul { - margin: 0; - padding: 0; - list-style: none; -} - -/* Definition Lists */ -dl { - overflow: hidden; - margin: 0 0 $space; -} - -dt { - font-weight: bold; -} - -dd { - margin-left: 0; -} \ No newline at end of file diff --git a/core/source/css/scss/base/_main.scss b/core/source/css/scss/base/_main.scss deleted file mode 100644 index f92fd1b34..000000000 --- a/core/source/css/scss/base/_main.scss +++ /dev/null @@ -1,6 +0,0 @@ -body { - background: $white; - font: 100%/1.5 $font; - -webkit-text-size-adjust: 100%; - color: $gray-dark; -} \ No newline at end of file diff --git a/core/source/css/scss/base/_media.scss b/core/source/css/scss/base/_media.scss deleted file mode 100644 index ec74c8ffc..000000000 --- a/core/source/css/scss/base/_media.scss +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------*\ - $MEDIA ELEMENTS -\*------------------------------------*/ - -/* Flexible Media */ -img, video, object { - max-width: 100%; - height: auto; -} - -iframe { - margin-bottom: $space; -} - -figure { - margin-bottom: $space; - img { - margin-bottom: $space-half; - } -} -figcaption { - font-style: italic; -} \ No newline at end of file diff --git a/core/source/css/scss/base/_tables.scss b/core/source/css/scss/base/_tables.scss deleted file mode 100644 index 1b2a2b2c4..000000000 --- a/core/source/css/scss/base/_tables.scss +++ /dev/null @@ -1,18 +0,0 @@ -/*------------------------------------*\ - $Table -\*------------------------------------*/ -table { - border-collapse: collapse; - border-spacing: 0; - border: 1px solid $gray; - width: 100%; -} -th { - text-align: left; - border: 1px solid $gray; - padding: 0.2em; -} -td { - border: 1px solid $gray; - padding: 0.2em; -} \ No newline at end of file diff --git a/core/source/css/scss/base/_text.scss b/core/source/css/scss/base/_text.scss deleted file mode 100644 index de17e3ec6..000000000 --- a/core/source/css/scss/base/_text.scss +++ /dev/null @@ -1,26 +0,0 @@ -/* Text-Related Elements */ -p { - margin-bottom: $space; -} - -/* Blockquote */ -blockquote { - font-style:italic; - border-left: 1px solid $gray; - color: $gray; - padding-left: $pad; - margin-bottom: $space; -} - -/* Horizontal Rule */ -hr { - border: 0; - height: 2px; - background: $gray-light-2; - margin: $space-double 0; -} - -abbr { - border-bottom: 1px dotted $gray; - cursor: help; -} \ No newline at end of file diff --git a/core/source/css/scss/generic/_mixins.scss b/core/source/css/scss/generic/_mixins.scss deleted file mode 100644 index 14d8a2a3d..000000000 --- a/core/source/css/scss/generic/_mixins.scss +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------*\ - $MIXINS -\*------------------------------------*/ - -/* CSS Transition - Usage: @include transition(width,0.3s,ease-out); - */ -@mixin transition($transition-property, $transition-time, $method) { - -webkit-transition: $transition-property $transition-time $method; - -moz-transition: $transition-property $transition-time $method; - -ms-transition: $transition-property $transition-time $method; - -o-transition: $transition-property $transition-time $method; - transition: $transition-property $transition-time $method; -} - -/* Rem Unit font sizes with relative fallback http:/seesparkbox.com/foundry/scss_rem_mixin_now_with_a_better_fallback - Usage: @include font-size(1, large); -*/ -@mixin font-size( $decimal-size, $keyword: null ) { - @if $keyword{ font-size: $keyword; } - @else { font-size: $decimal-size * $base-font-multiplier * 16px;} - font-size: $decimal-size * 1rem; -} \ No newline at end of file diff --git a/core/source/css/scss/generic/_reset.scss b/core/source/css/scss/generic/_reset.scss deleted file mode 100644 index e7807ec72..000000000 --- a/core/source/css/scss/generic/_reset.scss +++ /dev/null @@ -1,17 +0,0 @@ -/*------------------------------------*\ - $RESET -\*------------------------------------*/ - -/* Border-Box http:/paulirish.com/2012/box-sizing-border-box-ftw/ */ -* { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} -html, body, div, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, ol, ul, li, form, legend, label, table, header, footer, nav, section, figure { - margin: 0; - padding: 0; -} -header, footer, nav, section, article, hgroup, figure { - display: block; -} \ No newline at end of file diff --git a/core/source/css/scss/generic/_variables.scss b/core/source/css/scss/generic/_variables.scss deleted file mode 100644 index 3e7ccbcba..000000000 --- a/core/source/css/scss/generic/_variables.scss +++ /dev/null @@ -1,65 +0,0 @@ -/*------------------------------------*\ - $VARIABLES -\*------------------------------------*/ - -//Colors -$gray : #808080; -$gray-light : #f9f9f9; -$gray-light-2 : #eee; -$gray-light-3 : #ddd; -$gray-med : #666; -$gray-dark : #333; -$gray-dark-2 : #131313; -$white : #fff; -$black : #000; -$dim : rgba(0,0,0,0.5); -$error : #f00; -$valid : #089e00; -$warning : #fff664; -$information : #000db5; - - -//Typography -$font : "HelveticaNeue", "Helvetica", "Arial", sans-serif; -$font-secondary : Georgia, Times, "Times New Roman", serif; - -$font-size-small : 0.75em; -$font-size-small-2 : 0.875em; -$font-size-med : 1em; -$font-size-med-2: 1.0625em; -$font-size-large : 1.4375em; - - -//Layout -$max-width: 72em; - -//Defaults -$space : 1em; -$space-and-half : $space*1.5; -$space-double : $space*2; -$space-quad : $space*4; -$space-half : $space/2; -$pad : 1em; -$pad-and-half : $pad*1.5; -$pad-double : $pad*2; -$pad-half : $pad/2; -$pad-quarter :$pad/4; - -//Borders -$border-med: 3px; -$border-thick: 7px; - - -//Breakpoints -$bp-small : 24em; -$bp-small-2 : 29.75em; -$bp-small-3 : 39.8em; -$bp-med : 46.8em; -$bp-med-2 : 48em; -$bp-large : 50em; -$bp-large-2 : 66em; -$bp-xl : 73em; -$bp-xxl : 89em; -$bp-xxxl : 93em; - -// \ No newline at end of file diff --git a/core/source/css/scss/objects/_accordion.scss b/core/source/css/scss/objects/_accordion.scss deleted file mode 100644 index 3423544c9..000000000 --- a/core/source/css/scss/objects/_accordion.scss +++ /dev/null @@ -1,33 +0,0 @@ -.accordion { - margin-bottom: $space; -} - -.acc-handle { - background: $gray-dark-2; - color: $white; - font-family: $font-secondary; - font-weight: bold; - display: block; - position: relative; - padding: $pad-half; - border-bottom: 1px solid $gray-light-3; - - &:after { - content:"+"; - float: right; - } - - &:hover { - color: $white; - background: $gray-dark; - - } - - &.active { - background: $gray-dark; - - &:after { - content:"-"; - } - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_article.scss b/core/source/css/scss/objects/_article.scss deleted file mode 100644 index 55387309e..000000000 --- a/core/source/css/scss/objects/_article.scss +++ /dev/null @@ -1,34 +0,0 @@ -//Article -.article-header { - h1 { - font-size: 2.5em; - } -} - -.byline { - font-size: $font-size-small-2; - font-style: italic; - margin-bottom: $space-half; -} - -//Social Share -.social-share { - overflow: hidden; - margin-bottom: $space; - - li { - float: left; - margin-right: $space-half; - } - - a { - background: $gray-dark; - color: $white; - display: block; - padding: $pad-half; - - &:hover { - background: $gray; - } - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_blocks.scss b/core/source/css/scss/objects/_blocks.scss deleted file mode 100644 index 160963bd6..000000000 --- a/core/source/css/scss/objects/_blocks.scss +++ /dev/null @@ -1,138 +0,0 @@ -/* Generic Placeholder Brick: REMOVE FOR PRODUCTION */ -.brick { - background: #dcdddc; - padding: $pad-double; - text-align: center; - font-weight: bold; - border-bottom: 1px solid $gray-light-2; -} - -/* Block */ -.block { - overflow: hidden; - - p:last-child { - margin-bottom: 0; - } -} - -.headline { - line-height: 1.2; -} - -/* Hero Block */ -.block-hero { - margin-bottom: $space-half; - - .b-thumb { - img { - display: block; - } - } - - @media all and (min-width: $bp-large) { - position: relative; - - .b-text { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - background: $dim; - color: $white; - padding: $pad-and-half; - } - } -} - -/* Block Thumbnail with Headline */ -.block-thumb { - display: table; - width: 100%; - border-collapse: collapse; - - .b-inner { - display: table-row; - vertical-align: top; - overflow: hidden; - } - - .b-thumb { - @media all and (min-width: $bp-small-2) { - display: table-cell; - vertical-align: top; - width: 30%; - max-width: 10em; - - img { - display: block; - width: 100%; - height: auto; - } - } - } - - .b-text { - @media all and (min-width: $bp-small-2) { - display: table-cell; - width: 70%; - padding: 0 $pad; - } - } -} - - -/* Block Headline Summary */ -.block-headline-summary { - a { - display: block; - padding: $pad-half; - } -} - -/* Block Inset */ -.block-inset { - position: relative; - - .b-thumb { - position: relative; - z-index: 0; - - img { - display: block; - } - } -} - -/* Hero Block */ -.block-inset { - margin-bottom: $space-half; - position: relative; - - .headline { - font-size: 1.1em; - } - - .b-text { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - background: $dim; - color: $white; - padding: $pad-half; - } -} - -/* Block Thumb with Summary */ -.block-thumb-summary { - .b-thumb { - float: left; - width: 50%; - } - - .b-text { - margin-left: 50%; - padding: $pad-half; - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_buttons.scss b/core/source/css/scss/objects/_buttons.scss deleted file mode 100644 index 0ae2805e1..000000000 --- a/core/source/css/scss/objects/_buttons.scss +++ /dev/null @@ -1,40 +0,0 @@ -/*------------------------------------*\ - $BUTTONS -\*------------------------------------*/ -.btn { - display: inline-block; - background: $gray-dark; - color: $white; - line-height: 1; - font-weight: bold; - padding: $pad; - border: 0; - text-align: center; - - &:hover, &:focus { - background: $gray; - color: $white; - } - - &.disabled { - background: $gray-light-2; - color: $gray; - } -} - -.btn-small { - padding: $pad-half; -} - -.btn-large { - padding: $pad-half; - text-transform: uppercase; - background: $gray; - font-size: 1.4rem; - font-weight: normal; -} - - -.text-btn { - font-style: italic; -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_carousels.scss b/core/source/css/scss/objects/_carousels.scss deleted file mode 100644 index 77e3f197b..000000000 --- a/core/source/css/scss/objects/_carousels.scss +++ /dev/null @@ -1,38 +0,0 @@ -/* Horizontal Carousel */ -.carousel-horizontal { - margin-bottom: $space; - overflow: hidden; - position: relative; -} - -.carousel-island-container { - overflow: hidden; -} - -.carousel-container { - position: relative; -} - -.carousel-list { - -} - -.carousel-controls { - display: table; - width: 100%; - margin: (-$space-half) 0 $space; - - a, div { - display: table-cell; - padding: $pad-half; - } - - .carousel-pagination { - text-align: center; - } - - .carousel-next { - text-align: right; - } - -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_comments.scss b/core/source/css/scss/objects/_comments.scss deleted file mode 100644 index 9959b7abf..000000000 --- a/core/source/css/scss/objects/_comments.scss +++ /dev/null @@ -1,30 +0,0 @@ -.comments { - li { - margin-bottom: $space; - } -} - -.comment-container { - overflow: hidden; - margin-bottom: $space; - list-style: none; -} - -.comment-meta { - float: left; - width: 6.5em; - - img { - display: block; - border: 1px solid $gray-light-2; - margin-bottom: $space-half; - } -} - -.comment-name { - font-size: $font-size-small; -} - -.comment-text { - margin-left: 9em; -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_footer.scss b/core/source/css/scss/objects/_footer.scss deleted file mode 100644 index 850f94bf4..000000000 --- a/core/source/css/scss/objects/_footer.scss +++ /dev/null @@ -1,39 +0,0 @@ -/*------------------------------------*\ - $FOOTER -\*------------------------------------*/ -.footer { - clear: both; - overflow: hidden; - background: $gray-dark-2; - color: $white; - line-height: 1.2; - - a { - color: $gray-light; - } -} - -//Footer Nav -.nav-footer { - margin: (-$pad) (-$pad) $space; - - li { - border-bottom: 1px solid $gray-dark; - - @media all and (min-width: $bp-med) { - border: 0; - float: left; - } - } - - a { - display: block; - padding: $pad; - } -} - -.copyright { - @media all and (min-width: $bp-med) { - float: right; - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_header.scss b/core/source/css/scss/objects/_header.scss deleted file mode 100644 index 8fb78d7ce..000000000 --- a/core/source/css/scss/objects/_header.scss +++ /dev/null @@ -1,45 +0,0 @@ -.header { - background: $white; - padding: 0; - border-bottom: 1px solid #dbdbdb; - @extend .lc; -} - -.logo { - float: left; - max-width: 8rem; - margin: 0.4rem; - - @media all and (min-width: $bp-med) { - max-width: 9rem; - } -} - -.nav-toggle { - float: right; - display: block; - padding: 0.9rem 1rem 0.7rem; - font-size: 1.3rem; - line-height: 1; - border-left: 1px solid #dbdbdb; - - @media all and (min-width: $bp-med) { - display: none; - } - - @media all and (max-width: 17em) { - padding-left: 0.2rem; - padding-right: 0.2rem; - } -} - -.search-form { - overflow: hidden; - max-height: 0; - - @media all and (min-width: $bp-med) { - float: right; - max-height: none; - margin: 0.65em 0.5em 0 0; - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_icons.scss b/core/source/css/scss/objects/_icons.scss deleted file mode 100644 index cacbf1fbd..000000000 --- a/core/source/css/scss/objects/_icons.scss +++ /dev/null @@ -1,125 +0,0 @@ - -/* Icon Font */ -@font-face { - font-family: 'icons'; - src:url('../fonts/icons.eot'); - src:url('../fonts/icons.eot?#iefix') format('embedded-opentype'), - url('../fonts/icons.woff') format('woff'), - url('../fonts/icons.ttf') format('truetype'), - url('../fonts/icons.svg#icons') format('svg'); - font-weight: normal; - font-style: normal; -} - -/* Use the following CSS code if you want to use data attributes for inserting your icons */ -[data-icon]:before { - font-family: 'icons'; - content: attr(data-icon); - speak: none; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; -} - -.icon-twitter:before, .icon-stumbleupon:before, .icon-pinterest:before, .icon-linkedin:before, .icon-google-plus:before, .icon-search:before, .icon-play:before, .icon-menu:before, .icon-arrow-left:before, .icon-arrow-right:before, .icon-bubble:before, .icon-facebook:before, .icon-feed:before, .icon-youtube:before, .icon-tag:before, .icon-tumblr:before, .icon-instagram, .icon-podcast, .icon-apple,.icon-android, .icon-arrow:after, .icon-envelope:before { - font-family: 'icons'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - -webkit-font-smoothing: antialiased; -} -.icon-twitter:before { - content: "\74"; -} -.icon-stumbleupon:before { - content: "\75"; -} -.icon-pinterest:before { - content: "\70"; -} -.icon-linkedin:before { - content: "\69"; -} -.icon-google-plus:before { - content: "\67"; -} -.icon-search:before { - content: "\73"; -} -.icon-play:before { - content: "\61"; -} -.icon-menu:before { - content: "\21"; -} -.icon-arrow-left:before { - content: "\23"; -} -.icon-arrow-right:before { - content: "\24"; -} -.icon-bubble:before { - content: "\25"; -} -.icon-facebook:before { - content: "\66"; -} -.icon-feed:before { - content: "\27"; -} -.icon-youtube:before { - content: "\79"; -} -.icon-tag:before { - content: "\28"; -} -.icon-tumblr:before { - content: "\6d"; -} -.icon-instagram:before { - content: "\22"; -} -.icon-podcast:before { - content: "\26"; -} -.icon-android:before { - content: "\29"; -} -.icon-apple:before { - content: "\2a"; -} -.icon-envelope:before { - content: "\2b"; -} - - -.icon-arrow:after { - content: "\61"; - display: inline-block; - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform: rotate(90deg); -} - -.icon-play:before { - font-size: 0.7rem; - padding-left: 0.2em; -} - -.icon-play-box { - display: block; - margin-left: $pad-double; -} - -.icon-play-box:before { - padding: $pad-quarter; - background: $gray; - color: $white; - margin-left: -1.7rem; - margin-right: $pad-half; -} diff --git a/core/source/css/scss/objects/_layout.scss b/core/source/css/scss/objects/_layout.scss deleted file mode 100644 index 4929d28e3..000000000 --- a/core/source/css/scss/objects/_layout.scss +++ /dev/null @@ -1,288 +0,0 @@ -/*------------------------------------*\ - $LAYOUT -\*------------------------------------*/ - -/* Layout Container */ -.lc { - max-width: $max-width; - margin: 0 auto; - padding: $pad-half; -} - -/*------------------------------------*\ - $TEMPLATES -\*------------------------------------*/ - -/* Two Column Layout */ -.l-two-col { - @extend .cf; - - .l-main { - @media all and (min-width: $bp-large) { - float: left; - width: 70%; - padding-right: $pad; - } - } - - .l-sidebar { - @media all and (max-width: $bp-large) { - clear: both; - } - - @media all and (min-width: $bp-large) { - float: left; - width: 30%; - padding: 0 0 0 $pad; - } - } -} - - -/*------------------------------------*\ - $GRIDS -\*------------------------------------*/ - -/* Grid Container */ -.g { - overflow: hidden; - margin: 0 (-$pad-half); -} - -/* Grid Item */ -.gi { - padding: $pad-half; - - img { - display: block; - } - - @media all and (min-width: $bp-med) { - float: left; - } -} - -/* Grid 1up */ -.g-1up { - .gi { - width: 100%; - } -} - -/* Grid 2up */ -.g-2up { - @media all and (min-width: $bp-med) { - > .gi { - float: left; - width: 50%; - - &:nth-of-type(odd) { - clear: left; - } - } - } -} - -/* Grid Half (Always displayed side by side) */ -.g-half { - > .gi { - float: left; - width: 50%; - - &:nth-of-type(odd) { - clear: left; - } - } -} - -/* Grid 3up */ -.g-3up { - @media all and (min-width: $bp-med) { - > .gi { - float: left; - width: 50%; - - &:nth-of-type(2n+1) { - clear: left; - } - } - } - - @media all and (min-width: $bp-large) { - > .gi { - width: 33.3333333%; - - &:nth-of-type(2n+1) { - clear: none; - } - - &:nth-of-type(3n+1) { - clear: left; - } - } - } -} - -/* Grid 4up */ -.g-4up { - @media all and (min-width: $bp-med) { - >.gi { - float: left; - width: 50%; - - &:nth-of-type(2n+1) { - clear: both; - } - } - } - - @media all and (min-width: $bp-large) { - >.gi { - width: 25%; - - &:nth-of-type(2n+1) { - clear: none; - } - - &:nth-of-type(4n+1) { - clear: left; - } - } - } -} - -/* Grid Quarter (Always displayed side by side) */ -.g-quarter { - > .gi { - float: left; - width: 24%; - - &:nth-of-type(4n+1) { - clear: left; - } - } -} - -.g-max4 { - - @media all and (min-width: $bp-small-2) { - >.gi { - float: left; - width: 50%; - - &:nth-of-type(2n+1) { - clear: both; - } - } - } - - @media all and (min-width: $bp-small-3) { - >.gi { - width: 33.3333333%; - - &:nth-of-type(2n+1) { - clear: none; - } - - &:nth-of-type(3n+1) { - clear: left; - } - } - } - - @media all and (min-width: $bp-large) { - >.gi { - width: 25%; - - &:nth-of-type(3n+1) { - clear: none; - } - - &:nth-of-type(4n+1) { - clear: left; - } - } - } -} - -/* Grid 5up */ -.g-max5 { - - >.gi { - float: left; - width: 50%; - - &:nth-of-type(2n+1) { - clear: both; - } - } - - @media all and (min-width: $bp-small-3) { - >.gi { - width: 33.3333333%; - - &:nth-of-type(2n+1) { - clear: none; - } - - &:nth-of-type(3n+1) { - clear: left; - } - } - } - - @media all and (min-width: $bp-med) { - >.gi { - width: 25%; - - &:nth-of-type(3n+1) { - clear: none; - } - - &:nth-of-type(4n+1) { - clear: left; - } - } - } - - @media all and (min-width: $bp-med-2) { - >.gi { - width: 20%; - - &:nth-of-type(4n+1) { - clear: none; - } - - &:nth-of-type(5n+1) { - clear: left; - } - } - } -} - -/* Grid 2/3 */ -.gi-2-3 { - @media all and (min-width: $bp-med) { - float: left; - width: 66.666666%; - } -} - -.gi-1-3 { - @media all and (min-width: $bp-med) { - float: left; - width: 33.333333%; - } -} - -/* Grid 4up block */ -.g-opposites { - .gi { - float: left; - - &:last-child { - float: right; - text-align: right; - } - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_lists.scss b/core/source/css/scss/objects/_lists.scss deleted file mode 100644 index 0c4e8beaa..000000000 --- a/core/source/css/scss/objects/_lists.scss +++ /dev/null @@ -1,63 +0,0 @@ -.inline-list { - li { - display: inline-block; - } -} - -/* Social List */ -.social-list { - li { - margin: 0 0.4rem $space 0; - } - - a { - font-size: 1.6em; - } -} - - -/* Headline List */ -.headline-list { - margin-bottom: $space; - - &.flush { - margin: 0; - } - - h4 { - font-weight: normal; - } - - li { - padding: $pad-quarter 0; - border-top: 1px solid $gray-light-3; - } -} - -/* Post List */ -.post-list { - li { - margin-bottom: $space; - } -} - -/* Bullet List */ -.bullet-list { - list-style: square; - margin: 0 0 1em 1.2em; - line-height: 1.3; - - li { - margin-bottom: $space; - } -} - -/* Text List */ -.text-list { - margin: 0 0 1em; - line-height: 1.3; - - li { - margin-bottom: $space; - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_main.scss b/core/source/css/scss/objects/_main.scss deleted file mode 100644 index 592c01dea..000000000 --- a/core/source/css/scss/objects/_main.scss +++ /dev/null @@ -1,9 +0,0 @@ -/*------------------------------------*\ - $MAIN CONTENT AREA -\*------------------------------------*/ -[role=main] { - padding: $pad-half $pad-half $pad-double; - overflow: hidden; - @extend .lc; - @extend .cf; -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_messaging.scss b/core/source/css/scss/objects/_messaging.scss deleted file mode 100644 index a2d31a92e..000000000 --- a/core/source/css/scss/objects/_messaging.scss +++ /dev/null @@ -1,18 +0,0 @@ -/*------------------------------------*\ - $MESSAGING -\*------------------------------------*/ - -// Alerts -.alert { - text-align: center; - padding: $pad; - margin-bottom: $space-half; - border: 1px solid $gray; - background: $gray-light; -} - -.alert-error { - color: $error; - border-color: $error; - background: #ffbebe; -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_nav.scss b/core/source/css/scss/objects/_nav.scss deleted file mode 100644 index b98e3300e..000000000 --- a/core/source/css/scss/objects/_nav.scss +++ /dev/null @@ -1,52 +0,0 @@ -/*------------------------------------*\ - $NAVIGATION -\*------------------------------------*/ - -.nav { - clear: both; - overflow: hidden; - max-height: 0; - - a { - display: block; - padding: $pad; - border-top: 1px solid $gray-light-2; - } - - &.active { - max-height: 40em; - } - - @media all and (min-width: $bp-med) { - max-height: none; - float: right; - clear: none; - - li { - float: left; - } - - a { - border: 0; - } - - } -} - -//Pagination -.pagination { - overflow: hidden; - - li { - float: left; - border-right: 1px solid $gray-light-2; - - &:last-child { - border: 0; - } - } - - a { - padding: $pad; - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_sections.scss b/core/source/css/scss/objects/_sections.scss deleted file mode 100644 index 7235af263..000000000 --- a/core/source/css/scss/objects/_sections.scss +++ /dev/null @@ -1,7 +0,0 @@ -.section { - margin: 0 0 $space; -} - -.section-title { - margin-bottom: $space-half; -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_tabs.scss b/core/source/css/scss/objects/_tabs.scss deleted file mode 100644 index b22ca7438..000000000 --- a/core/source/css/scss/objects/_tabs.scss +++ /dev/null @@ -1,33 +0,0 @@ -.tabs { - overflow: hidden; - - ul { - display: table; - width: 100%; - } - - li { - display: table-cell; - text-align: center; - border-right: 1px solid $gray-light-3; - - &:last-child { - border-right: 0; - } - } - - a { - display: block; - padding: $pad-half; - background: $gray; - - &:hover, &:focus { - background: $gray-light-3; - } - - &.active { - background: $gray-dark; - color: $white; - } - } -} \ No newline at end of file diff --git a/core/source/css/scss/objects/_text.scss b/core/source/css/scss/objects/_text.scss deleted file mode 100644 index d645f5646..000000000 --- a/core/source/css/scss/objects/_text.scss +++ /dev/null @@ -1,41 +0,0 @@ -//Intro text -.intro { - font-size: $font-size-med-2; - font-weight: bold; -} - -//Pullquote -.pullquote { - font-family: $font-secondary; - font-size: $font-size-large; -} - -//Caption -.caption { - font-style: italic; -} - -//Passages of text -.text { - a { - text-decoration: underline; - } - - ul { - list-style: disc; - margin: 0 0 $space 1.2em; - - ul { - margin-bottom: 0; - } - } - - ol { - list-style: decimal; - margin: 0 0 $space 1.5em; - - ol { - margin-bottom: 0; - } - } -} diff --git a/core/source/css/scss/objects/_tooltip.scss b/core/source/css/scss/objects/_tooltip.scss deleted file mode 100644 index e0fdd95da..000000000 --- a/core/source/css/scss/objects/_tooltip.scss +++ /dev/null @@ -1,42 +0,0 @@ -.tooltip-container { - display: inline-block; - position: relative; - - &:hover { - .tooltip { - display: block; - } - } -} - -.tooltip-link { - background: $gray-light; -} - -.tooltip { - display: none; - position: absolute; - top: 1.5em; - left: 0; - width: 18em; - padding: $pad; - background: $white; - border: 1px solid $gray; - box-shadow: 0.3em 0.3em 1em 0 rgba(0,0,0,0.2); - - h2 { - margin-top: 0; - } - - @media all and (min-width: $bp-small) { - width: 22em; - } - - @media all and (min-width: $bp-small-2) { - width: 27em; - } - - @media all and (min-width: $bp-small-3) { - width: 30em; - } -} \ No newline at end of file diff --git a/core/source/css/style.css b/core/source/css/style.css deleted file mode 100644 index 67f12faf0..000000000 --- a/core/source/css/style.css +++ /dev/null @@ -1,1103 +0,0 @@ -/* - ,, ,, ,, - .M"""bgd mm db `7MM mm mm `7MM OO OO OO -,MI "Y MM MM MM MM MM 88 88 88 -`MMb. mmMMmm ,pW"Wq.`7MMpdMAo. `7Mb,od8 `7MM .P"Ybmmm MMpMMMb.mmMMmm mmMMmm MMpMMMb. .gP"Ya `7Mb,od8 .gP"Ya || || || - `YMMNq. MM 6W' `Wb MM `Wb MM' "' MM :MI I8 MM MM MM MM MM MM ,M' Yb MM' "',M' Yb || || || -. `MM MM 8M M8 MM M8 MM MM WmmmP" MM MM MM MM MM MM 8M"""""" MM 8M"""""" `' `' `' -Mb dM MM YA. ,A9 MM ,AP MM MM 8M MM MM MM MM MM MM YM. , MM YM. , ,, ,, ,, -P"Ybmmd" `Mbmo`Ybmd9' MMbmmd' .JMML. .JMML.YMMMMMb .JMML JMML.`Mbmo `Mbmo.JMML JMML.`Mbmmd'.JMML. `Mbmmd' db db db - MM 6' dP - .JMML. Ybmmmd' - -Pattern Lab doesn't have any CSS requirements, which means you can write your styles however you want. Hooray! -You can use Sass, Less, vanilla CSS, or some other crazy thing I haven't heard of yet. -So please don't use these styles. They're just here to put together the demo, and nothing more. -They're intentionally gray, boring, and crappy because you're supposed to do this stuff yourself. - -Atomic design is philosophically complimentary with these CSS approaches: - -* SMACSS by Jonathan Snook http://smacss.com/ -* OOCSS by Nicole Sullivan http://oocss.org/ -* BEM CSS Methology : http://bem.info/method/ -* CSS Guidelines by Harry Roberts : https://github.com/csswizardry/CSS-Guidelines - -So feel free to use any of these approaches. Or don't. It's totally up to you. - -*/ -/*------------------------------------*\ - $TABLE OF CONTENTS - based generally on Harry Roberts excellent CSS Guidelines https://github.com/csswizardry/CSS-Guidelines -\*------------------------------------*/ -/** - * VARIABLES..............................Declarations of Sass variables - * .....Colors - * .....Typography - * .....Layout - * .....Defaults - * .....Breakpoints - * MIXINS.................................Sass mixins - * RESET..................................Set reset defaults - * GLOBAL CLASSES.........................Set reset defaults - * GLOBAL ELEMENTS........................Establish global styles - * .....Main - * .....Headings - * .....Text-related elements (p, blockquote, lists) - * .....Defaults - * .....Breakpoints - * TYPOGRAPHY------------------------------ - * MEDIA------------------------------ - * LAYOUT------------------------------ - * NAVIGATION------------------------------ - * TOC To Be Continued - */ -/*------------------------------------*\ - $VARIABLES -\*------------------------------------*/ -/*------------------------------------*\ - $MIXINS -\*------------------------------------*/ -/* CSS Transition - Usage: @include transition(width,0.3s,ease-out); - */ -/* Rem Unit font sizes with relative fallback http:/seesparkbox.com/foundry/scss_rem_mixin_now_with_a_better_fallback - Usage: @include font-size(1, large); -*/ -/*------------------------------------*\ - $RESET -\*------------------------------------*/ -/* Border-Box http:/paulirish.com/2012/box-sizing-border-box-ftw/ */ -* { - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; } - -html, body, div, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, ol, ul, li, form, legend, label, table, header, footer, nav, section, figure { - margin: 0; - padding: 0; } - -header, footer, nav, section, article, hgroup, figure { - display: block; } - -/*------------------------------------*\ - $GLOBAL ELEMENTS -\*------------------------------------*/ -/*------------------------------------*\ - $GLOBAL CLASSES -\*------------------------------------*/ -/* Clearfix */ -.cf, .l-two-col, [role=main] { - *zoom: 1; } - -.cf:before, .l-two-col:before, [role=main]:before, .cf:after, .l-two-col:after, [role=main]:after { - content: " "; - /* 1 */ - display: table; - /* 2 */ } - -.cf:after, .l-two-col:after, [role=main]:after { - clear: both; } - -/* Completely remove from the flow and screen readers. */ -.is-hidden { - display: none !important; - visibility: hidden !important; } - -/* Completely remove from the flow but leave available to screen readers. */ -.is-vishidden { - position: absolute !important; - overflow: hidden; - width: 1px; - height: 1px; - padding: 0; - border: 0; - clip: rect(1px, 1px, 1px, 1px); } - -/* Floats */ -.right { - float: right; - padding: 0 0 1em 1em; } - -.right-search { - float: right; - padding: 0 0 1em 0; } - -.left { - float: left; - padding: 0 1em 1em 0; } - -/* Text-Align */ -.align-right { - text-align: right; } - -.align-center { - text-align: center; } - -.align-left { - text-align: left; } - -/* Display Classes */ -@media all and (max-width: 46.8em) { - .hide-small { - display: none; } } - -@media all and (min-width: 46.8em) and (max-width: 50em) { - .hide-med { - display: none; } } - -@media all and (min-width: 50em) { - .hide-large { - display: none; } } - -.valid { - color: #089e00; } - -.error { - color: red; } - -.warning { - color: #fff664; } - -.information { - color: #000db5; } - -.font-secondary { - font-family: Georgia, Times, "Times New Roman", serif; } - -body { - background: white; - font: 100%/1.5 "HelveticaNeue", "Helvetica", "Arial", sans-serif; - -webkit-text-size-adjust: 100%; - color: #333333; } - -/* Links */ -a { - color: #333333; - text-decoration: none; - outline: 0; } - a:hover, a:focus { - color: gray; } - -/* Headings */ -/*Further Reading: http:/csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css/ */ -h1, .alpha { - line-height: 1.2; } - -h2, .beta { - line-height: 1.2; } - -h3, .gamma { - line-height: 1.2; } - -/* Subheadings */ -.subheading { - font-family: Georgia, Times, "Times New Roman", serif; - font-weight: normal; } - -/* Text-Related Elements */ -p { - margin-bottom: 1em; } - -/* Blockquote */ -blockquote { - font-style: italic; - border-left: 1px solid gray; - color: gray; - padding-left: 1em; - margin-bottom: 1em; } - -/* Horizontal Rule */ -hr { - border: 0; - height: 2px; - background: #eeeeee; - margin: 2em 0; } - -abbr { - border-bottom: 1px dotted gray; - cursor: help; } - -ol, ul { - margin: 0; - padding: 0; - list-style: none; } - -/* Definition Lists */ -dl { - overflow: hidden; - margin: 0 0 1em; } - -dt { - font-weight: bold; } - -dd { - margin-left: 0; } - -/*------------------------------------*\ - $MEDIA ELEMENTS -\*------------------------------------*/ -/* Flexible Media */ -img, video, object { - max-width: 100%; - height: auto; } - -iframe { - margin-bottom: 1em; } - -figure { - margin-bottom: 1em; } - figure img { - margin-bottom: 0.5em; } - -figcaption { - font-style: italic; } - -/*------------------------------------*\ - $FORMS -\*------------------------------------*/ -form ol, form ul { - list-style: none; - margin-left: 0; } - -fieldset { - border: 0; - padding: 0; - margin: 0; } - -label { - display: block; - padding-bottom: 0.5em; } - -button, input, select, textarea { - font-family: inherit; - font-size: 100%; - margin: 0 1px 0; } - -input, textarea { - width: 100%; - border: 1px solid gray; - padding: 0.5em 0.65rem; } - -input[type=text], input[type=search], input[type=url], input[type=number], textarea { - -webkit-appearance: none; } - -button, input[type="submit"] { - padding: 0.5em; - background: #333333; - border: 1px solid gray; - cursor: pointer; } - -input[type="checkbox"], -input[type="radio"] { - width: auto; - margin-right: 0.3em; } - -input[type="search"] { - -webkit-appearance: none; - border-radius: 0; } - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; } - -.field-container { - margin-bottom: 1em; } - -.inline-form fieldset, .inline-form .inline-container { - position: relative; } -.inline-form input[type=submit], .inline-form button, .inline-form .btn { - font-size: 0.875em; - padding: 0.65rem 1.3rem; - background: #333333; - position: absolute; - top: 0; - right: 0; - z-index: 1; - width: auto; } - .inline-form input[type=submit]:hover, .inline-form input[type=submit]:focus, .inline-form button:hover, .inline-form button:focus, .inline-form .btn:hover, .inline-form .btn:focus { - background: gray; - color: white; } - -/* Validation */ -.has-error { - border-color: red; } - -.is-valid { - border-color: #089e00; } - -/*------------------------------------*\ - $SPECIFIC FORMS -\*------------------------------------*/ -/* Search Form */ -.search-field { - padding-right: 3em; } - -.inline-form .search-submit { - background: none; - padding: 0.78em 1em; - border: 0; - border-left: 1px solid gray; - color: gray; } - -/*------------------------------------*\ - $Table -\*------------------------------------*/ -table { - border-collapse: collapse; - border-spacing: 0; - border: 1px solid gray; - width: 100%; } - -th { - text-align: left; - border: 1px solid gray; - padding: 0.2em; } - -td { - border: 1px solid gray; - padding: 0.2em; } - -.animate-fade { - -webkit-transition: opacity 0.3s ease-out; - -moz-transition: opacity 0.3s ease-out; - -ms-transition: opacity 0.3s ease-out; - -o-transition: opacity 0.3s ease-out; - transition: opacity 0.3s ease-out; } - .animate-fade:hover { - opacity: 0; } - -.animate-move > .demo-shape { - -webkit-transition: all 0.8s ease-in-out; - -moz-transition: all 0.8s ease-in-out; - -ms-transition: all 0.8s ease-in-out; - -o-transition: all 0.8s ease-in-out; - transition: all 0.8s ease-in-out; } - -/*------------------------------------*\ - $LAYOUT -\*------------------------------------*/ -/*------------------------------------*\ - $LAYOUT -\*------------------------------------*/ -/* Layout Container */ -.lc, .header, [role=main] { - max-width: 72em; - margin: 0 auto; - padding: 0.5em; } - -/*------------------------------------*\ - $TEMPLATES -\*------------------------------------*/ -/* Two Column Layout */ -@media all and (min-width: 50em) { - .l-two-col .l-main { - float: left; - width: 70%; - padding-right: 1em; } } -@media all and (max-width: 50em) { - .l-two-col .l-sidebar { - clear: both; } } -@media all and (min-width: 50em) { - .l-two-col .l-sidebar { - float: left; - width: 30%; - padding: 0 0 0 1em; } } - -/*------------------------------------*\ - $GRIDS -\*------------------------------------*/ -/* Grid Container */ -.g { - overflow: hidden; - margin: 0 -0.5em; } - -/* Grid Item */ -.gi { - padding: 0.5em; } - .gi img { - display: block; } - @media all and (min-width: 46.8em) { - .gi { - float: left; } } - -/* Grid 1up */ -.g-1up .gi { - width: 100%; } - -/* Grid 2up */ -@media all and (min-width: 46.8em) { - .g-2up > .gi { - float: left; - width: 50%; } - .g-2up > .gi:nth-of-type(odd) { - clear: left; } } - -/* Grid Half (Always displayed side by side) */ -.g-half > .gi { - float: left; - width: 50%; } - .g-half > .gi:nth-of-type(odd) { - clear: left; } - -/* Grid 3up */ -@media all and (min-width: 46.8em) { - .g-3up > .gi { - float: left; - width: 50%; } - .g-3up > .gi:nth-of-type(2n+1) { - clear: left; } } -@media all and (min-width: 50em) { - .g-3up > .gi { - width: 33.3333333%; } - .g-3up > .gi:nth-of-type(2n+1) { - clear: none; } - .g-3up > .gi:nth-of-type(3n+1) { - clear: left; } } - -/* Grid 4up */ -@media all and (min-width: 46.8em) { - .g-4up > .gi { - float: left; - width: 50%; } - .g-4up > .gi:nth-of-type(2n+1) { - clear: both; } } -@media all and (min-width: 50em) { - .g-4up > .gi { - width: 25%; } - .g-4up > .gi:nth-of-type(2n+1) { - clear: none; } - .g-4up > .gi:nth-of-type(4n+1) { - clear: left; } } - -/* Grid Quarter (Always displayed side by side) */ -.g-quarter > .gi { - float: left; - width: 24%; } - .g-quarter > .gi:nth-of-type(4n+1) { - clear: left; } - -@media all and (min-width: 29.75em) { - .g-max4 > .gi { - float: left; - width: 50%; } - .g-max4 > .gi:nth-of-type(2n+1) { - clear: both; } } -@media all and (min-width: 39.8em) { - .g-max4 > .gi { - width: 33.3333333%; } - .g-max4 > .gi:nth-of-type(2n+1) { - clear: none; } - .g-max4 > .gi:nth-of-type(3n+1) { - clear: left; } } -@media all and (min-width: 50em) { - .g-max4 > .gi { - width: 25%; } - .g-max4 > .gi:nth-of-type(3n+1) { - clear: none; } - .g-max4 > .gi:nth-of-type(4n+1) { - clear: left; } } - -/* Grid 5up */ -.g-max5 > .gi { - float: left; - width: 50%; } - .g-max5 > .gi:nth-of-type(2n+1) { - clear: both; } -@media all and (min-width: 39.8em) { - .g-max5 > .gi { - width: 33.3333333%; } - .g-max5 > .gi:nth-of-type(2n+1) { - clear: none; } - .g-max5 > .gi:nth-of-type(3n+1) { - clear: left; } } -@media all and (min-width: 46.8em) { - .g-max5 > .gi { - width: 25%; } - .g-max5 > .gi:nth-of-type(3n+1) { - clear: none; } - .g-max5 > .gi:nth-of-type(4n+1) { - clear: left; } } -@media all and (min-width: 48em) { - .g-max5 > .gi { - width: 20%; } - .g-max5 > .gi:nth-of-type(4n+1) { - clear: none; } - .g-max5 > .gi:nth-of-type(5n+1) { - clear: left; } } - -/* Grid 2/3 */ -@media all and (min-width: 46.8em) { - .gi-2-3 { - float: left; - width: 66.666666%; } } - -@media all and (min-width: 46.8em) { - .gi-1-3 { - float: left; - width: 33.333333%; } } - -/* Grid 4up block */ -.g-opposites .gi { - float: left; } - .g-opposites .gi:last-child { - float: right; - text-align: right; } - -/*------------------------------------*\ - $PAGE STRUCTURE -\*------------------------------------*/ -.header { - background: white; - padding: 0; - border-bottom: 1px solid #dbdbdb; } - -.logo { - float: left; - max-width: 8rem; - margin: 0.4rem; } - @media all and (min-width: 46.8em) { - .logo { - max-width: 9rem; } } - -.nav-toggle { - float: right; - display: block; - padding: 0.9rem 1rem 0.7rem; - font-size: 1.3rem; - line-height: 1; - border-left: 1px solid #dbdbdb; } - @media all and (min-width: 46.8em) { - .nav-toggle { - display: none; } } - @media all and (max-width: 17em) { - .nav-toggle { - padding-left: 0.2rem; - padding-right: 0.2rem; } } - -.search-form { - overflow: hidden; - max-height: 0; } - @media all and (min-width: 46.8em) { - .search-form { - float: right; - max-height: none; - margin: 0.65em 0.5em 0 0; } } - -/*------------------------------------*\ - $NAVIGATION -\*------------------------------------*/ -.nav { - clear: both; - overflow: hidden; - max-height: 0; } - .nav a { - display: block; - padding: 1em; - border-top: 1px solid #eeeeee; } - .nav.active { - max-height: 40em; } - @media all and (min-width: 46.8em) { - .nav { - max-height: none; - float: right; - clear: none; } - .nav li { - float: left; } - .nav a { - border: 0; } } - -.pagination { - overflow: hidden; } - .pagination li { - float: left; - border-right: 1px solid #eeeeee; } - .pagination li:last-child { - border: 0; } - .pagination a { - padding: 1em; } - -/*------------------------------------*\ - $MAIN CONTENT AREA -\*------------------------------------*/ -[role=main] { - padding: 0.5em 0.5em 2em; - overflow: hidden; } - -/*------------------------------------*\ - $FOOTER -\*------------------------------------*/ -.footer { - clear: both; - overflow: hidden; - background: #131313; - color: white; - line-height: 1.2; } - .footer a { - color: #f9f9f9; } - -.nav-footer { - margin: -1em -1em 1em; } - .nav-footer li { - border-bottom: 1px solid #333333; } - @media all and (min-width: 46.8em) { - .nav-footer li { - border: 0; - float: left; } } - .nav-footer a { - display: block; - padding: 1em; } - -@media all and (min-width: 46.8em) { - .copyright { - float: right; } } - -/*------------------------------------*\ - $TEXT Styles -\*------------------------------------*/ -.intro { - font-size: 1.0625em; - font-weight: bold; } - -.pullquote { - font-family: Georgia, Times, "Times New Roman", serif; - font-size: 1.4375em; } - -.caption { - font-style: italic; } - -.text a { - text-decoration: underline; } -.text ul { - list-style: disc; - margin: 0 0 1em 1.2em; } - .text ul ul { - margin-bottom: 0; } -.text ol { - list-style: decimal; - margin: 0 0 1em 1.5em; } - .text ol ol { - margin-bottom: 0; } - -/*------------------------------------*\ - $COMPONENTS -\*------------------------------------*/ -/* Icon Font */ -@font-face { - font-family: 'icons'; - src: url("../fonts/icons.eot"); - src: url("../fonts/icons.eot?#iefix") format("embedded-opentype"), url("../fonts/icons.woff") format("woff"), url("../fonts/icons.ttf") format("truetype"), url("../fonts/icons.svg#icons") format("svg"); - font-weight: normal; - font-style: normal; } - -/* Use the following CSS code if you want to use data attributes for inserting your icons */ -[data-icon]:before { - font-family: 'icons'; - content: attr(data-icon); - speak: none; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; } - -.icon-twitter:before, .icon-stumbleupon:before, .icon-pinterest:before, .icon-linkedin:before, .icon-google-plus:before, .icon-search:before, .icon-play:before, .icon-menu:before, .icon-arrow-left:before, .icon-arrow-right:before, .icon-bubble:before, .icon-facebook:before, .icon-feed:before, .icon-youtube:before, .icon-tag:before, .icon-tumblr:before, .icon-instagram, .icon-podcast, .icon-apple, .icon-android, .icon-arrow:after, .icon-envelope:before { - font-family: 'icons'; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - -webkit-font-smoothing: antialiased; } - -.icon-twitter:before { - content: "\74"; } - -.icon-stumbleupon:before { - content: "\75"; } - -.icon-pinterest:before { - content: "\70"; } - -.icon-linkedin:before { - content: "\69"; } - -.icon-google-plus:before { - content: "\67"; } - -.icon-search:before { - content: "\73"; } - -.icon-play:before { - content: "\61"; } - -.icon-menu:before { - content: "\21"; } - -.icon-arrow-left:before { - content: "\23"; } - -.icon-arrow-right:before { - content: "\24"; } - -.icon-bubble:before { - content: "\25"; } - -.icon-facebook:before { - content: "\66"; } - -.icon-feed:before { - content: "\27"; } - -.icon-youtube:before { - content: "\79"; } - -.icon-tag:before { - content: "\28"; } - -.icon-tumblr:before { - content: "\6d"; } - -.icon-instagram:before { - content: "\22"; } - -.icon-podcast:before { - content: "\26"; } - -.icon-android:before { - content: "\29"; } - -.icon-apple:before { - content: "\2a"; } - -.icon-envelope:before { - content: "\2b"; } - -.icon-arrow:after { - content: "\61"; - display: inline-block; - -webkit-transform: rotate(90deg); - -moz-transform: rotate(90deg); - -ms-transform: rotate(90deg); - -o-transform: rotate(90deg); - transform: rotate(90deg); } - -.icon-play:before { - font-size: 0.7rem; - padding-left: 0.2em; } - -.icon-play-box { - display: block; - margin-left: 2em; } - -.icon-play-box:before { - padding: 0.25em; - background: gray; - color: white; - margin-left: -1.7rem; - margin-right: 0.5em; } - -/*------------------------------------*\ - $BUTTONS -\*------------------------------------*/ -.btn { - display: inline-block; - background: #333333; - color: white; - line-height: 1; - font-weight: bold; - padding: 1em; - border: 0; - text-align: center; } - .btn:hover, .btn:focus { - background: gray; - color: white; } - .btn.disabled { - background: #eeeeee; - color: gray; } - -.btn-small { - padding: 0.5em; } - -.btn-large { - padding: 0.5em; - text-transform: uppercase; - background: gray; - font-size: 1.4rem; - font-weight: normal; } - -.text-btn { - font-style: italic; } - -/* Generic Placeholder Brick: REMOVE FOR PRODUCTION */ -.brick { - background: #dcdddc; - padding: 2em; - text-align: center; - font-weight: bold; - border-bottom: 1px solid #eeeeee; } - -/* Block */ -.block { - overflow: hidden; } - .block p:last-child { - margin-bottom: 0; } - -.headline { - line-height: 1.2; } - -/* Hero Block */ -.block-hero { - margin-bottom: 0.5em; } - .block-hero .b-thumb img { - display: block; } - @media all and (min-width: 50em) { - .block-hero { - position: relative; } - .block-hero .b-text { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - background: rgba(0, 0, 0, 0.5); - color: white; - padding: 1.5em; } } - -/* Block Thumbnail with Headline */ -.block-thumb { - display: table; - width: 100%; - border-collapse: collapse; } - .block-thumb .b-inner { - display: table-row; - vertical-align: top; - overflow: hidden; } - @media all and (min-width: 29.75em) { - .block-thumb .b-thumb { - display: table-cell; - vertical-align: top; - width: 30%; - max-width: 10em; } - .block-thumb .b-thumb img { - display: block; - width: 100%; - height: auto; } } - @media all and (min-width: 29.75em) { - .block-thumb .b-text { - display: table-cell; - width: 70%; - padding: 0 1em; } } - -/* Block Headline Summary */ -.block-headline-summary a { - display: block; - padding: 0.5em; } - -/* Block Inset */ -.block-inset { - position: relative; } - .block-inset .b-thumb { - position: relative; - z-index: 0; } - .block-inset .b-thumb img { - display: block; } - -/* Hero Block */ -.block-inset { - margin-bottom: 0.5em; - position: relative; } - .block-inset .headline { - font-size: 1.1em; } - .block-inset .b-text { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - background: rgba(0, 0, 0, 0.5); - color: white; - padding: 0.5em; } - -/* Block Thumb with Summary */ -.block-thumb-summary .b-thumb { - float: left; - width: 50%; } -.block-thumb-summary .b-text { - margin-left: 50%; - padding: 0.5em; } - -.inline-list li { - display: inline-block; } - -/* Social List */ -.social-list li { - margin: 0 0.4rem 1em 0; } -.social-list a { - font-size: 1.6em; } - -/* Headline List */ -.headline-list { - margin-bottom: 1em; } - .headline-list.flush { - margin: 0; } - .headline-list h4 { - font-weight: normal; } - .headline-list li { - padding: 0.25em 0; - border-top: 1px solid #dddddd; } - -/* Post List */ -.post-list li { - margin-bottom: 1em; } - -/* Bullet List */ -.bullet-list { - list-style: square; - margin: 0 0 1em 1.2em; - line-height: 1.3; } - .bullet-list li { - margin-bottom: 1em; } - -/* Text List */ -.text-list { - margin: 0 0 1em; - line-height: 1.3; } - .text-list li { - margin-bottom: 1em; } - -.tooltip-container { - display: inline-block; - position: relative; } - .tooltip-container:hover .tooltip { - display: block; } - -.tooltip-link { - background: #f9f9f9; } - -.tooltip { - display: none; - position: absolute; - top: 1.5em; - left: 0; - width: 18em; - padding: 1em; - background: white; - border: 1px solid gray; - box-shadow: 0.3em 0.3em 1em 0 rgba(0, 0, 0, 0.2); } - .tooltip h2 { - margin-top: 0; } - @media all and (min-width: 24em) { - .tooltip { - width: 22em; } } - @media all and (min-width: 29.75em) { - .tooltip { - width: 27em; } } - @media all and (min-width: 39.8em) { - .tooltip { - width: 30em; } } - -.accordion { - margin-bottom: 1em; } - -.acc-handle { - background: #131313; - color: white; - font-family: Georgia, Times, "Times New Roman", serif; - font-weight: bold; - display: block; - position: relative; - padding: 0.5em; - border-bottom: 1px solid #dddddd; } - .acc-handle:after { - content: "+"; - float: right; } - .acc-handle:hover { - color: white; - background: #333333; } - .acc-handle.active { - background: #333333; } - .acc-handle.active:after { - content: "-"; } - -.tabs { - overflow: hidden; } - .tabs ul { - display: table; - width: 100%; } - .tabs li { - display: table-cell; - text-align: center; - border-right: 1px solid #dddddd; } - .tabs li:last-child { - border-right: 0; } - .tabs a { - display: block; - padding: 0.5em; - background: gray; } - .tabs a:hover, .tabs a:focus { - background: #dddddd; } - .tabs a.active { - background: #333333; - color: white; } - -.section { - margin: 0 0 1em; } - -.section-title { - margin-bottom: 0.5em; } - -.article-header h1 { - font-size: 2.5em; } - -.byline { - font-size: 0.875em; - font-style: italic; - margin-bottom: 0.5em; } - -.social-share { - overflow: hidden; - margin-bottom: 1em; } - .social-share li { - float: left; - margin-right: 0.5em; } - .social-share a { - background: #333333; - color: white; - display: block; - padding: 0.5em; } - .social-share a:hover { - background: gray; } - -.comments li { - margin-bottom: 1em; } - -.comment-container { - overflow: hidden; - margin-bottom: 1em; - list-style: none; } - -.comment-meta { - float: left; - width: 6.5em; } - .comment-meta img { - display: block; - border: 1px solid #eeeeee; - margin-bottom: 0.5em; } - -.comment-name { - font-size: 0.75em; } - -.comment-text { - margin-left: 9em; } - -/*------------------------------------*\ - $MESSAGING -\*------------------------------------*/ -.alert { - text-align: center; - padding: 1em; - margin-bottom: 0.5em; - border: 1px solid gray; - background: #f9f9f9; } - -.alert-error { - color: red; - border-color: red; - background: #ffbebe; } diff --git a/core/source/css/style.scss b/core/source/css/style.scss deleted file mode 100644 index b749f20f7..000000000 --- a/core/source/css/style.scss +++ /dev/null @@ -1,121 +0,0 @@ -/* - ,, ,, ,, - .M"""bgd mm db `7MM mm mm `7MM OO OO OO -,MI "Y MM MM MM MM MM 88 88 88 -`MMb. mmMMmm ,pW"Wq.`7MMpdMAo. `7Mb,od8 `7MM .P"Ybmmm MMpMMMb.mmMMmm mmMMmm MMpMMMb. .gP"Ya `7Mb,od8 .gP"Ya || || || - `YMMNq. MM 6W' `Wb MM `Wb MM' "' MM :MI I8 MM MM MM MM MM MM ,M' Yb MM' "',M' Yb || || || -. `MM MM 8M M8 MM M8 MM MM WmmmP" MM MM MM MM MM MM 8M"""""" MM 8M"""""" `' `' `' -Mb dM MM YA. ,A9 MM ,AP MM MM 8M MM MM MM MM MM MM YM. , MM YM. , ,, ,, ,, -P"Ybmmd" `Mbmo`Ybmd9' MMbmmd' .JMML. .JMML.YMMMMMb .JMML JMML.`Mbmo `Mbmo.JMML JMML.`Mbmmd'.JMML. `Mbmmd' db db db - MM 6' dP - .JMML. Ybmmmd' - -Pattern Lab doesn't have any CSS requirements, which means you can write your styles however you want. Hooray! -You can use Sass, Less, vanilla CSS, or some other crazy thing I haven't heard of yet. -So please don't use these styles. They're just here to put together the demo, and nothing more. -They're intentionally gray, boring, and crappy because you're supposed to do this stuff yourself. - -Atomic design is philosophically complimentary with these CSS approaches: - -* SMACSS by Jonathan Snook http://smacss.com/ -* OOCSS by Nicole Sullivan http://oocss.org/ -* BEM CSS Methology : http://bem.info/method/ -* CSS Guidelines by Harry Roberts : https://github.com/csswizardry/CSS-Guidelines - -So feel free to use any of these approaches. Or don't. It's totally up to you. - -*/ - -/*------------------------------------*\ - $TABLE OF CONTENTS - based generally on Harry Roberts excellent CSS Guidelines https://github.com/csswizardry/CSS-Guidelines -\*------------------------------------*/ -/** - * VARIABLES..............................Declarations of Sass variables - * .....Colors - * .....Typography - * .....Layout - * .....Defaults - * .....Breakpoints - * MIXINS.................................Sass mixins - * RESET..................................Set reset defaults - * GLOBAL CLASSES.........................Set reset defaults - * GLOBAL ELEMENTS........................Establish global styles - * .....Main - * .....Headings - * .....Text-related elements (p, blockquote, lists) - * .....Defaults - * .....Breakpoints - * TYPOGRAPHY------------------------------ - * MEDIA------------------------------ - * LAYOUT------------------------------ - * NAVIGATION------------------------------ - * TOC To Be Continued - */ - - - -@import "scss/generic/variables"; -@import "scss/generic/mixins"; -@import "scss/generic/reset"; - - - - - -/*------------------------------------*\ - $GLOBAL ELEMENTS -\*------------------------------------*/ -@import "scss/base/global-classes"; -@import "scss/base/main"; -@import "scss/base/links"; -@import "scss/base/headings"; -@import "scss/base/text"; -@import "scss/base/lists"; -@import "scss/base/media"; -@import "scss/base/forms"; -@import "scss/base/tables"; -@import "scss/base/animation"; - - - - - -/*------------------------------------*\ - $LAYOUT -\*------------------------------------*/ -@import "scss/objects/layout"; - - -/*------------------------------------*\ - $PAGE STRUCTURE -\*------------------------------------*/ -@import "scss/objects/header"; -@import "scss/objects/nav"; -@import "scss/objects/main"; -@import "scss/objects/footer"; - - - -/*------------------------------------*\ - $TEXT Styles -\*------------------------------------*/ -@import "scss/objects/text"; - - -/*------------------------------------*\ - $COMPONENTS -\*------------------------------------*/ -@import "scss/objects/icons"; -@import "scss/objects/buttons"; -@import "scss/objects/blocks"; -@import "scss/objects/lists"; -@import "scss/objects/tooltip"; -@import "scss/objects/accordion"; -@import "scss/objects/tabs"; -@import "scss/objects/sections"; -@import "scss/objects/article"; -@import "scss/objects/comments"; -@import "scss/objects/messaging"; - - diff --git a/core/source/favicon.ico b/core/source/favicon.ico deleted file mode 100644 index eee4aa78fdf0366fc7278096cdafb710d2dbb49c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32988 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 diff --git a/core/source/fonts/icons.dev.svg b/core/source/fonts/icons.dev.svg deleted file mode 100644 index ca8e49e3a..000000000 --- a/core/source/fonts/icons.dev.svg +++ /dev/null @@ -1,103 +0,0 @@ - - - - -This is a custom SVG font generated by IcoMoon. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/source/fonts/icons.eot b/core/source/fonts/icons.eot deleted file mode 100644 index 81a314b08656fbc88f07e28a48ebc64f4a3f2881..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4372 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 - - - -This is a custom SVG font generated by IcoMoon. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/source/fonts/icons.ttf b/core/source/fonts/icons.ttf deleted file mode 100644 index e6bff0814789fd9c456398e60a6bfd25b5669c07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4216 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 diff --git a/core/source/images/favicon_16x16.jpg b/core/source/images/favicon_16x16.jpg deleted file mode 100644 index 05e744965e4c5b6e5bfc151c0c9ac8bca5d401cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 311 zcma)$%?g4*6ot>^IGT<+g9@TT`<@|)pceKRWkIxP71X{5bJ0WevuYo`!fk3%kefcZ zaQH494xQ+RY_eM|fvW1m#TU9mkGJ#n9t_;*0YDFgqk1!2pl*y|bj1OJb8d5C+oCJX z`K}PIFFnuqJgMXxCBsNXp^W1siQ}>;in8~CIgS&^AX6&KQ=R6Y$G?FZXcr2~Vj3OI eEY0YMshRx)YZ=utx@G7BX)#l5X$)VDbbbO1IVAo7 diff --git a/core/source/images/favicon_32x32.jpg b/core/source/images/favicon_32x32.jpg deleted file mode 100644 index 0970980b7db94f921f3036ba2bfb94cfeefa5672..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 320 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 diff --git a/core/source/images/fpo_4x3.png b/core/source/images/fpo_4x3.png deleted file mode 100644 index 7ff84b81be949f855d71d99d8ecf6632004a17a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3709 zcmb_edpy&7A6FrlaGXN9)XolFWM*tGv*j==(nz&ggx_MhZ_F?bIc;eokxR<0lWWI@ zo!rgk>_{CUMHU*Kl8T8OcfvD0b)M&s^E`h&zdwGz&-e5Cyx*Vq_j7r@epgRA9#c}# zRFIOAQnI(RJtZZzl_({(<#)Ml5|4D*F@of?hmIxEPg5_@BSi_izp>W;6N|*q$Rs+Ic7{p~{Mx;fzEnCj)R!6z!kjh*9mJ8m{3x4_ zLz~6@qLobZ3nzOYrBQ=GpR0rP`x^xaa|<}k8Vk2EH8GZ`H#Wy$ZLnt6=C-!x#>Q|g z4EzP_{WmxNf<^pSEL0)}y4l+QX!Tc@WPUcyzXn%w_-pjZ6v@2PB!fMCWxPqU6pWK6 z&R~D~>8FyClFZD^yu7@dH*ebLY(`5mi!WckoRE;fWHPT_y?XcV-Ak7)Wn^UJi; z)PjP7YuBzNCnw*&eLE{FD=seX#*G^ufgT1@QVId~w$^7DgOfvI*TLhvWuHo43VxVh zCLEk4jcn*pFmeja{5MAS!j)%1(SwuU&90D*O>A~;`MlHr_ty{KG{;_DTx-m3Fl-!g z|CGA?K<#ze)M&5ni*mIR%}3OkKABPJw51P6>H-@lTb5$V&mph#XHj?M434Wk| zUlZtgzk3MgyqX5-*|pcMIj)vm`Cl9KCyqNwbMD)Gvkspx*$SOM$14qX4{bu(pPFo2 zKR{GVI`{iU^%nhgnv)DC%Q0Z>=a`XW0{y;sQhWD$nCg!+4>0le2swmH(?gFx9k+`k z-YZrYdCbN#@zyO$mYE#$DMf-kNBPo;XTo$2t)!|!o13d1?KfNKtMubuq&D}CfUB>Q zTOaf>ZaSE^)wLp;GB|g5WC%b9wAOho3TV(6A6A&^bM-{8owZ#$K4GTB?V2<7mfb9; zAqB#K`-}E-Z8Nz*hA5)d@oi~Nm$Hr$A$V0u+=)L_!Os?~Xt1-newO92W~{I(odQR7 zuCeUL+#LjH-I^uhUFl^{NLoAX9hgz#<>v{!{=oee!@u2_V&Q~UxF(62`}CHTd!uSG zMVcde!a3m5N(jR|VuppD3P`1>bIsTFh-2X%I`u2VFX>UFCmA9<~`aA%FoKl%x!P|J!^@!7vt2ZZxFTS1Vk1>GY+u?@| zdM5{9dvs7JcznWMR`uhK;hKIW(~xXM!kCkLwFZ~8m*uS8{pUB@C{&dpB58jsirAYQ zbeV&?*1JtFz<^ky)xyD7y0Q>mi6@MA@5p;&;E|c#)Y|dNNUQAt=ha7Jr)~ok!u?gN z$SAh<&E7pa@`QeeTbbLZFF%`Nzu!^YMqcY1CPj_p1Wt4vtne1MAo@jW>>2KeXXosX zZ$^hs@rI{;wq<4sW11Ou59z28)GJ8MVrj=(>pbt5J3&rF%2D4|h8CCoxGF0U(}?z6yVoyShXxWg(!f1jE6?MfeK;C#%F z0{zIQLLq=I^sdf3X=k}n4HTU|Jr2aA1~%8Ph|)e<F38uky4KIAI#yw!Nh5c4zLD|d`b#`iCPx-X0IAdH^PzrXbE>l3= zmbVi-y51tZ3>l$q1e*-*Bf|z&gy1`H_sn|(E6I%pj?!ZHCmScS{Lq4^oX9&B)4?6l z)sZ|$(J;w0d>UHmOfc^67b$4GOfh_+_(R$O?9^|QlSu&nB+uVXopEn}ueXUj;<0}3 zSnfqU8$%zp(8Mi1RWWQR;lp-Unw}Mm&#%uSd`Hyy{3DYpu`QaZi@d zX3nd-$RH}7tJH6(Z$HK>-A&4Hzkh|n+_wgn@`N;I*lC8AmEuhxqsJ{*o&Y9hG?(LDnDZ{Y(huBn zX)zzbi(DiNp-VhAcijJNSc+iK5^HL6T$*zk2=k8vrgts-Wa=^T47Q%Tf{-@7OD2cF zE8SO>uFCD0b(>=>S!&725#-mx@aHkpn8RnC;VX3(t!ry*rp`WLWk5z_Q$=ZN*wN4^ zl?|kdu-7CvRM-skeVki2;R4uaYj*+M6N>;{BAcsOyF!1sYS7jD38hGo@>*1v?0=lF1rkX{HvgIVq6WY} z>wi6R@vU2kAVC=gRv=*1>8wL%$J;C4)YZ(MS^D_s(WH3Qm7nh}Xz+Et z1kpXq?^RmfbL>|@ZNWdQUK$_Bdp-MP$(c7Hg1IcQMT?1n?$hh}3;S-H$^<-b{8me3 z+EWkoy)^T5)#Tdo^oJm)FSf5Z@oZwyeKmEtdg5dN-5W5`y)fXW&5U;bAW!JSHT#p= zHRXgR_N>?jlYN6{c&WV7>nVLTc zboRO0QF0ZC_yT8GbGI?ij>mM9TNX02NxH;t5n`DQPVsh^BgALp6sFUr6^Yphz6SzL4AHKw^2S#? zTYSsiMhHnvs}7`>XX$VO8vY4elsPaA`%?XixwX~maIu7Swe

    }0pf-(XDQCC+&s3FzW5ip2~GC~Ee%p2-Q5Nar-I!Z+Y z^5cT?ve6tIQC0>X-;s2mX_8AhlZZ&c{v)xcwXZu7ZbkH@deI2HjdMJ(k;)yVPb1>!RGKxF>iTmP zEuE=!s;4v69iop`ha5J?5lC(u4*72gb90obndG~tFEfA zs;921ia;2uXzCm48EWWi8W?D*sHhnteqs%%1TPBFjs6qs@HbZNU$Gm_Kyl|~HXzbS z7l;lE;|@~icUZoGb{@fw@f?IFoKVMNOl)`merLBYYntE;PPYisY`y$cTyXD}FCF89Wb z8)0E#At50*Z{BQgZ=ar?UR+$v&(EKknMp`UxN_x6L_|bwZEaIiQ%On5+}vDBN=i~v z5|hdN^yyPuTU%sgFMg~x^?STQBhG$Ow5-rUvhJEi;IgV zCnsB5TXS-9Mn^|yXJ;QgcrZ3LR##Vd=gyt1tSlCbH8C-9{rdHxp`p;w(5kAcsHmv- z@82&iEe#G1Mn^}drlxjxcfWo6wzRbL!-o%PX=$%tzkdGw`L%1;I2_Ksd-pOjGCDds zDl04V^76{d%afCnJ3BicK73eTUtd;M_Tt5h#KgqDzP_thuQoO|4i67kS6AP@eLF5L zuA-tMGc%LTW;Zl6q^GB6XJ;=gECdDyuCK2jj_5f80tv^P8t7X4489+BLccMQ2*k-w z1cJ->_9igev?f?)m|p&l@tbe5(?%}6ZnQbqcTR?2mipm(z@-MXkxA;QSlRM3P6$2% zLhAe2q5Q)j{2!eXO|+`Dv|VFzlumzt^?rZpX>=86&p4HoI3pJKdloCcZ6Bbya>G$}x zVDNie6{of}($MLJiDp|A!3t&x4O1?sBKvv`HD>PBq+8NsnS{P=@o}mfV6Ds z4or6NqV%&bH$`lp=mipoGlNP&yD^ZUB=d~Ak1Oo@u%<-K>G(a`yNsTxVv>o)Z}j zkz)9kLC{&9x@aV0D_KaDvq^=y`7to_h&8y4prx(?8>){Q<10H_1#l->D|2|S^v0>W zs6~Dmj+8y~5bsOs!%j=A<7mEg`~@LcyA5i$7M&&%E~jh^b;s^vT$3yp<#^0z?OZ z_!y{)GJ?LmE!%_LnI!q))}^W(>q9RKU_F`&Oz;a}H8k`@O3m=xMdLiq`Osl(>*y?) zI0`>4J4($WHq0+_b0L8?2Em5-LV=T8<071TOz5#W(xxTRAo6L6ddygx>-N0!Ejg;6 zVslK7FWMqFXLLoPr z?2yTdg^7TdHIGH_XeUctzuU4;s^D(4#33x#Lx0_jakle(8NmqaOFp*R-*t19J^n5R z$;H)oA*07b#h<3)!7tThRszs|{R_^P+ORKlSZ-3@H64X^r@s^3?m zh-7ZD5WpT_2|X+6YH=oj?;Z;)Z(l*^nC!~8Ju2o~+~~nJ+M$^I2bwi82{ZSY7>T)e z67xyp8LP$S!;D1?x}kk-lS^fSECXpL*k?20j0K+;2rCSqBQ#%NZIzDt&~%mH`!z7f zUmR&GUbw?G;c}~>-Dl95i;`JDwNv2zV*5mZ3ad+56xzpWLt!c0O~2V568GtFvcy$K zfySY;5;(8^K<#{iEy-W^eEc$K7&LZNNL1&71h(%&kWnRa3>ffC@AbWgXCBw?&a2MY zNmdX~Y(FXAnRDV3TfNiaf}C=bt{ZM%M%g>H35_ z*lgX{-u>9P+VXY(xY~W1oZf?zy>G3$fsXW(9ejjr-JISaJXo%@;kcOa<*||=`%i}d)dk7W^%ikGTrQ3f3m_dF?hpj5bK5JeYEky5mp+e_wOpfor!h0{I7|7xZUm9)5ca+~}F{l2stGY8q@Kr3>c_r|&?IKm1r zbB;~QT?)0~=PE9Tl>UYUb=1rYPm85k=D-2FvTeYHC*m;G0089uSKVL~?JWbj;y(Q)8DF#sb2ZOcZ5`*&!H1$#o8N0G46gj0H+$Zjc>=ogp;ed-^ z?77onxYKrv1b^YDEA|@6Xrx&F)U&8Xk;l`gs)36_crHeclSqUlBW?C^wx;x-@CV>A zO@6>GjzY?SiwnTeg cD?tLF`Qsjt<<=nnjh_TlLkojKJ#6rQ0935D#Q*>R diff --git a/core/source/images/fpo_square.png b/core/source/images/fpo_square.png deleted file mode 100644 index ce8a1e4e6300201c394e9f87ffdb7d5cdef5952b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8694 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 z 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 diff --git a/core/source/images/sample/landscape-16x9-mountains.jpg b/core/source/images/sample/landscape-16x9-mountains.jpg deleted file mode 100644 index 3c0d92b29f77787ab7ffa1319e0c7fec99e505fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78249 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
    DL9OyoT08p!08Nn`fF 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 ED{nS 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

    9bGii+O&+vz@tiumbaR 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°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%0AAPC!@ti36iIDtbSP=-cP<_4qZxk)6 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 F|JhtSvljpW diff --git a/core/source/images/sample/thumb-square-river.jpg b/core/source/images/sample/thumb-square-river.jpg deleted file mode 100644 index f190186895546e50637764384c67ee2a1c7fdf31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50522 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

    NLbh+tp)uwONS!8Ek5l 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 CyX1lZ diff --git a/core/source/images/sample/tout-4x3-climber.jpg b/core/source/images/sample/tout-4x3-climber.jpg deleted file mode 100644 index 1348cd7e743d0965b15a5771a116b0d48fdf3ef8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114565 zcmeFacUTllw>R2D&PbFD5)~w8kenswjKVOaVP=3K37cd<1pz?;2?{6~0m(^3B`P^* z0a0>NlH}WiVY}b+o^#&kyU+K>eXfOFy=v8}>Q$>kSC7A{IvF{cVdq!#cXa{)U0r|| z00062A3_OWgA@e(10W0l?kNob4iLs)v>W974;d^_Mi>BnfDOF7AR?zUHAu(Zb_H<2 zJD3ZQ3tj^N(P1b6@SOR1S8_x-z}O62P~IrC3(A8{#aM#vyskae6^UV707Qg@MP-Df zWJDy`ghgaT#AL)p!E6BRbjaWOrDOek23BY~_D>ozjS&PL9t8V`EZ*;Bfw2Ew76|X} zWx;~{%0mR}SKe%!-?IZW!8@irr}vYQlU0D^v=moVbOFf88bEd$0vtmzawp@!O@M@e zfPj#IgpiPgf|!Vyf`*)ggq((sii(DcijIQh)cv@95B#eSAtfdzB_lmUMs|jpjEsyL z5j^VH zj1pxIP8y>(4x(aS*Lzj)x#EmBX1+kt;f!+XpJ~ z?5v}jsf0J|<1ZEddkuL0R^em}Ai+L;CIz4XP|@|wjE9oN&9G{99!|ddvJfPVY*tzGgZcU4mlJG4$``~ z*J@);_FLMKh1Yv*7sg(ze4_k@e6u#$;GDhQSGMP}T1Pv-+UimIrSy%>YJx(-K~QH! z!@}cr=Y4VB6|N`Qov$GR4;XV6-yQ28d6EnGRxULuk&(dFpCtsxri83EQTB8-;tMqD zTEj!#u3hghuCKGK+iTcLo9>xXdy}k54BmNI_cUfB7gCCQf02zmUoej^+vIdv1 z^mJI&GMclR4s07x4ZX#F^(R2u;ahT*{Z>813E)=6U z2d_?m%+HbWhS@jN_t3pd_}81q2ggpF)T_DN;qDi+5q1knaZee zZpcY2Rr)Z^2I1!D3K7qe@n5BQG&;8_!z@k1=s6ROy?5KC{}T(abpixVuABgoDhpMj zBoiHn?ziz*VP{1c=&yPfY<<(bzlmbg29e3i&*WHn7zmXStdEZ?r+bmgjt0Ne`t zIIGB2;obg)*wip!cPUBLe8X4dJVGjb-_DEoML2{eE8l|NMCADc zQ{<$|(LnX`tDGS0Bj36$GrsaqmeH(=i6(NL{JRMJ&3c86^57F-zOTsqZVcSo2`Xp9 zNazt5J!{-XI%V3R-(M+l0xT)a&2c(X%ZWg-LwCvfAM7Nv@HvN% zH8K95#Cvv-?zhGAOs8>&M>ki!~gG>R%c_t$kAtM_ZllkEu55D5wLpbCk}@rJj; ziqJe(D4Uqz!aCFZkpA=C#FnwwWIH0(D<}mORF9RM=(#7AI|om`G3p%5?&kL|93!U! z+FIyrwR2Lt5yc3jIN5Az@pqh1zr@L!ra?HJQwtnXFIOmpOUP zopTe$aaq~k?SHsaF&IAann9Zfm3YJ>0OiX*OI%WnTVXXUGAyY))LJWvLat9(x8_w9O#5i;z@~Q#p0i)4@EEmkeNlDsvDyf&bP_-JZLxNsckG+&*tqp!1)76l z{~~E+Y3n7O*oO1-5W?k^u5jd2G9HI6qRF=(E7mu{FSn_Vf7>nkGj0vJG=$7I97c07t ztwU0wX?tK>yk#jTs=$-;>5PKw7G%k2CJ@jtXpKN@U!H&Jx-=rdHlMXaIc`kEvVBJ12YdN5TTwf!Ko zD%*!vh;(E}a_AXqUy|jueox2|uKM%1ajT`&rQ&^EnesTYTxhe6Y}cT>-H(!Qc*&~1>g`!C+XPSn(y#_gVHwNTIq|U=JZ!@<%Hhd#a>vm zID#&9Iq6$6Ut!*^C^xh%b`@F{c&0wQH+URhQMQ%4mkyci4>4{;=@;iiZ3J7-(9JE) zToqni^&Lj_?+ILf*njuD>#czGBN!{CH{7n7J&t)jGN}?b#7~f8#>MbTSfrFPHPe1Tw3*S-E?L z_CL(kEeLlN(--bDo&c%3*n@fL@0uxMl&%r(#kA9%0A_qAKo>IN{qi&6d149vtR2X! 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�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%0D#&pMl(aEDIhT2-Z8;voDwNUarK)Q89SY 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

    +QE&u6U6VQnAjbs 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 diff --git a/core/source/js/fitvids.js b/core/source/js/fitvids.js deleted file mode 100644 index e2c5fbe7f..000000000 --- a/core/source/js/fitvids.js +++ /dev/null @@ -1,77 +0,0 @@ -/*global jQuery */ -/*! -* FitVids 1.0 -* -* Copyright 2011, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com -* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ -* Released under the WTFPL license - http://sam.zoy.org/wtfpl/ -* -* Date: Thu Sept 01 18:00:00 2011 -0500 -*/ - -(function( $ ){ - - $.fn.fitVids = function( options ) { - var settings = { - customSelector: null - } - - var div = document.createElement('div'), - ref = document.getElementsByTagName('base')[0] || document.getElementsByTagName('script')[0]; - - div.className = 'fit-vids-style'; - div.innerHTML = '­'; - - ref.parentNode.insertBefore(div,ref); - - if ( options ) { - $.extend( settings, options ); - } - - return this.each(function(){ - var selectors = [ - "iframe[src*='player.vimeo.com']", - "iframe[src*='www.youtube.com']", - "iframe[src*='www.kickstarter.com']", - "object", - "embed" - ]; - - if (settings.customSelector) { - selectors.push(settings.customSelector); - } - - var $allVideos = $(this).find(selectors.join(',')); - - $allVideos.each(function(){ - var $this = $(this); - if (this.tagName.toLowerCase() == 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } - var height = ( this.tagName.toLowerCase() == 'object' || $this.attr('height') ) ? $this.attr('height') : $this.height(), - width = $this.attr('width') ? $this.attr('width') : $this.width(), - aspectRatio = height / width; - if(!$this.attr('id')){ - var videoID = 'fitvid' + Math.floor(Math.random()*999999); - $this.attr('id', videoID); - } - $this.wrap('

    ').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+"%"); - $this.removeAttr('height').removeAttr('width'); - }); - }); - } -})( jQuery ); \ No newline at end of file diff --git a/core/source/js/init.js b/core/source/js/init.js deleted file mode 100644 index fe973aa86..000000000 --- a/core/source/js/init.js +++ /dev/null @@ -1,26 +0,0 @@ -(function(w){ - var sw = document.body.clientWidth, - sh = document.body.clientHeight; - - $(w).resize(function(){ //Update dimensions on resize - sw = document.body.clientWidth; - sh = document.body.clientHeight; - - //updateAds(); - }); - - - //Navigation toggle - $('.nav-toggle-menu').click(function(e) { - e.preventDefault(); - $(this).toggleClass('active'); - $('.nav').toggleClass('active'); - }); - - //Navigation toggle - $('.nav-toggle-search').click(function(e) { - e.preventDefault(); - $(this).toggleClass('active'); - $('.header .search-form').toggleClass('active'); - }); -})(this); \ No newline at end of file diff --git a/core/source/js/jquery-2.0.0b2.js b/core/source/js/jquery-2.0.0b2.js deleted file mode 100644 index af8b3b87a..000000000 --- a/core/source/js/jquery-2.0.0b2.js +++ /dev/null @@ -1,8690 +0,0 @@ -/*! - * jQuery JavaScript Library v2.0.0b2 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2013-3-1 - */ -(function( window, undefined ) { - -// Can't do this because several apps including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -//"use strict"; -var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Support: IE9 - // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` - core_strundefined = typeof undefined, - - // Use the correct document accordingly with window argument (sandbox) - location = window.location, - document = window.document, - docElem = document.documentElement, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // [[Class]] -> type pairs - class2type = {}, - - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], - - core_version = "2.0.0b2", - - // Save a reference to some core methods - core_concat = core_deletedIds.concat, - core_push = core_deletedIds.push, - core_slice = core_deletedIds.slice, - core_indexOf = core_deletedIds.indexOf, - core_toString = class2type.toString, - core_hasOwn = class2type.hasOwnProperty, - core_trim = core_version.trim, - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Used for matching numbers - core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - - // Used for splitting on whitespace - core_rnotwhite = /\S+/g, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }, - - // The ready event handler and self cleanup method - completed = function() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: core_version, - - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return core_slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; - }, - - slice: function() { - return this.pushStack( core_slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), - - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray, - - isWindow: function( obj ) { - return obj != null && obj == obj.window; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - if ( obj == null ) { - return String( obj ); - } - // Support: Safari <=5.1 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ core_toString.call(obj) ] || "object" : - typeof obj; - }, - - isPlainObject: function( obj ) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - // Support: Firefox >16 - // The try/catch suppresses exceptions thrown when attempting to access - // the "constructor" property of certain host objects, ie. |window.location| - try { - if ( obj.constructor && - !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - } catch ( e ) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // keepScripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, keepScripts ) { - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - context = context || document; - - var parsed = rsingleTag.exec( data ), - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ); - - if ( scripts ) { - jQuery( scripts ).remove(); - } - - return jQuery.merge( [], parsed.childNodes ); - }, - - parseJSON: JSON.parse, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE9 - try { - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - globalEval: function( data ) { - var indirect = eval; - if ( jQuery.trim( data ) ) { - indirect( data + ";" ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - trim: function( text ) { - return text == null ? "" : core_trim.call( text ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - core_push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : core_indexOf.call( arr, elem, i ); - }, - - merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; - - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var retVal, - ret = [], - i = 0, - length = elems.length; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return core_concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = core_slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - - now: Date.now -}); - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); - - if ( jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || type !== "function" && - ( length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj ); -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( list && ( !fired || stack ) ) { - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); -jQuery.support = (function( support ) { - var input = document.createElement("input"), - fragment = document.createDocumentFragment(), - div = document.createElement("div"), - select = document.createElement("select"), - opt = select.appendChild( document.createElement("option") ); - - // Finish early in limited environments - if ( !input.type ) { - return support; - } - - input.type = "checkbox"; - - // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) - support.checkOn = input.value === ""; - - // Must access the parent to make an option select properly - // Support: IE9, IE10 - support.optSelected = opt.selected; - - // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode - support.boxModel = document.compatMode === "CSS1Compat"; - - // Will be defined later - support.reliableMarginRight = true; - support.boxSizingReliable = true; - support.pixelPosition = false; - - // Make sure checked status is properly cloned - // Support: IE9, IE10 - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Check if an input maintains its value after becoming a radio - // Support: IE9, IE10, Opera - input = document.createElement("input"); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; - - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "checked", "t" ); - input.setAttribute( "name", "t" ); - - fragment.appendChild( input ); - - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: Firefox 17+ - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - support.focusinBubbles = "onfocusin" in window; - - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - // Run tests that need a body at doc ready - jQuery(function() { - var container, marginDiv, - divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", - body = document.getElementsByTagName("body")[ 0 ]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - container = document.createElement("div"); - container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; - - // Check box-sizing and margin behavior - body.appendChild( container ).appendChild( div ); - div.innerHTML = ""; - div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; - - support.boxSizing = div.offsetWidth === 4; - support.doesNotIncludeMarginInBodyOffset = body.offsetTop !== 1; - - // Use window.getComputedStyle because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. (#3333) - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = div.appendChild( document.createElement("div") ); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); - } - - body.removeChild( container ); - }); - - return support; -})( {} ); - -/* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var data_user, data_priv, - rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; - -function Data() { - this.cache = {}; - this.expando = jQuery.expando + Math.random(); -} - -Data.uid = 1; - -Data.prototype = { - key: function( owner ) { - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; - - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; - descriptor[ this.expando ] = { value: unlock }; - - // Secure it in a non-enumerable, non-writable property - try { - Object.defineProperties( owner, descriptor ); - - // Support: Android<4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } - } - - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } - - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Support an expectation from the old data system where plain - // objects used to initialize would be set to the cache by - // reference, instead of having properties and values copied. - // Note, this will kill the connection between - // "this.cache[ unlock ]" and "cache" - if ( jQuery.isEmptyObject( cache ) ) { - this.cache[ unlock ] = data; - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - - return this; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. - var cache = this.cache[ this.key( owner ) ]; - - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { - return this.get( owner, key ); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, name, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - if ( key === undefined ) { - this.cache[ unlock ] = {}; - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat( key.map( jQuery.camelCase ) ); - } else { - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = jQuery.camelCase( key ); - name = name in cache ? - [ name ] : ( name.match( core_rnotwhite ) || [] ); - } - } - - i = name.length; - while ( i-- ) { - delete cache[ name[i] ]; - } - } - }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ this.key( owner ) ] - ); - }, - discard: function( owner ) { - delete this.cache[ this.key( owner ) ]; - } -}; - -// This will be used by remove()/cleanData() in manipulation to sever -// remaining references to node objects. One day we'll replace the dual -// arrays with a WeakMap and this won't be an issue. -// (Splices the data objects out of the internal cache arrays) -function data_discard( owner ) { - data_user.discard( owner ); - data_priv.discard( owner ); -} - -// These may be used throughout the jQuery core codebase -data_user = new Data(); -data_priv = new Data(); - - -jQuery.extend({ - // This is no longer relevant to jQuery core, but must remain - // supported for the sake of jQuery 1.9.x API surface compatibility. - acceptData: function() { - return true; - }, - - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - return data_user.remove( elem, name ); - }, - - // TODO: Replace all calls to _data and _removeData with direct - // calls to - // - // data_priv.access( elem, name, data ); - // - // data_priv.remove( elem, name ); - // - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - return data_priv.remove( elem, name ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var attrs, name, - elem = this[0], - i = 0, - data = null; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); - - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - attrs = elem.attributes; - for ( ; i < attrs.length; i++ ) { - name = attrs[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.substring(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); - } - - return jQuery.access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); - - // Get the Data... - if ( value === undefined ) { - - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; - } - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, key, undefined ); - if ( data !== undefined ) { - return data; - } - - // As a last resort, attempt to find - // the data by checking AGAIN, but with - // a camelCased key. - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return undefined; - } - - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); - - // *... In the case of properties that might ACTUALLY - // have dashes, we need to also store a copy of that - // unchanged property. - if ( /-/.test( key ) && data !== undefined ) { - data_user.set( this, key, value ); - } - }); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? - JSON.parse( data ) : data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray(data) ) { - queue = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - hooks.cur = fn; - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return jQuery._data( elem, key ) || jQuery._data( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - jQuery._removeData( elem, type + "queue" ); - jQuery._removeData( elem, key ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while( i-- ) { - tmp = jQuery._data( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, - rclass = /[\t\r\n]/g, - rreturn = /\r/g, - rfocusable = /^(?:input|select|textarea|button|object)$/i, - rclickable = /^(?:a|area)$/i, - rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call( this, j, this.className ) ); - }); - } - - if ( proceed ) { - // The disjunction here is for better compressibility (see removeClass) - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - " " - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - elem.className = jQuery.trim( cur ); - - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = arguments.length === 0 || typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call( this, j, this.className ) ); - }); - } - if ( proceed ) { - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - "" - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - elem.className = value ? jQuery.trim( cur ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.match( core_rnotwhite ) || []; - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - // Toggle whole class name - } else if ( type === core_strundefined || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // If the element has a class name or if we're passed "false", - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val, - self = jQuery(this); - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, option, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? - max : - one ? index : 0; - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // oldIE doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - // Don't return options that are disabled or in a disabled optgroup - ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && - ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attr: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === core_strundefined ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - - } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; - } - - } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - // In IE9+, Flash objects don't have .getAttribute (#12945) - // Support: IE9+ - if ( typeof elem.getAttribute !== core_strundefined ) { - ret = elem.getAttribute( name ); - } - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var name, propName, - i = 0, - attrNames = value && value.match( core_rnotwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { - propName = jQuery.propFix[ name ] || name; - - // Boolean attributes get special treatment (#10870) - // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) ) { - elem[ propName ] = false; - } - - elem.removeAttribute( name ); - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - return elem.getAttribute( name ) !== null ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); - -// IE9/10 do not see a selected option inside an optgroup unless you access it -// Support: IE9, IE10 -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - } - }); -} -var rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery._removeData( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = core_hasOwn.call( event, "type" ) ? event.type : event, - namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = core_slice.call( arguments ), - handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, matches, sel, handleObj, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - for ( ; cur != this; cur = cur.parentNode || this ) { - - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: Chrome 23+, Safari? - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { - this.click(); - return false; - } - } - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== document.activeElement && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === document.activeElement && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 10+ - if ( event.result !== undefined ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } -}; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && e.preventDefault ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && e.stopPropagation ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -// Support: Chrome 15+ -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// Create "bubbling" focus and blur events -// Support: Firefox 10+ -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); -/*! - * Sizzle CSS Selector Engine - * Copyright 2013 jQuery Foundation and other contributors - * Released under the MIT license - * http://sizzlejs.com/ - */ -(function( window, undefined ) { - -var i, - cachedruns, - Expr, - getText, - isXML, - compile, - outermostContext, - recompare, - sortInput, - - // Local document vars - setDocument, - document, - docElem, - documentIsXML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - support = {}, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - hasDuplicate = false, - sortOrder = function() { return 0; }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Array methods - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - operators = "([*^$|!~]?=)", - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - // Prefer arguments quoted, - // then not containing pseudos/brackets, - // then attribute selectors/non-parenthetical expressions, - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rsibling = /[\x20\t\r\n\f]*[+~]/, - - rnative = /^[^{]+\{\s*\[native code/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rescape = /'|\\/g, - rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g, - funescape = function( _, escaped ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - return high !== high ? - escaped : - // BMP codepoint - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -/** - * For feature detection - * @param {Function} fn The function to test for native support - */ -function isNative( fn ) { - return rnative.test( fn + "" ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var cache, - keys = []; - - return (cache = function( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key += " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key ] = value); - }); -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // release memory in IE - div = null; - } -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( !documentIsXML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && !rbuggyQSA.test(selector) ) { - old = true; - nid = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && context.parentNode || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Detect xml - * @param {Element|Object} elem An element or a document - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var doc = node ? node.ownerDocument || node : preferredDoc; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsXML = isXML( doc ); - - // Check if getElementsByTagName("*") returns only elements - support.tagNameNoComments = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Check if attributes should be retrieved by attribute nodes - support.attributes = assert(function( div ) { - div.innerHTML = ""; - var type = typeof div.lastChild.getAttribute("multiple"); - // IE8 returns a string for some attributes even when not present - return type !== "boolean" && type !== "string"; - }); - - // Check if getElementsByClassName can be trusted - support.getByClassName = assert(function( div ) { - // Opera can't find a second classname (in 9.6) - div.innerHTML = ""; - if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { - return false; - } - - // Safari 3.2 caches class attributes and doesn't catch changes - div.lastChild.className = "e"; - return div.getElementsByClassName("e").length === 2; - }); - - // Check if getElementsByName privileges form controls or returns elements by ID - // If so, assume (for broader support) that getElementById returns elements by name - support.getByName = assert(function( div ) { - // Inject content - div.id = expando + 0; - // Support: Windows 8 Native Apps - // Assigning innerHTML with "name" attributes throws uncatchable exceptions - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx - div.appendChild( document.createElement("a") ).setAttribute( "name", expando ); - div.appendChild( document.createElement("i") ).setAttribute( "name", expando ); - docElem.appendChild( div ); - - // Test - var pass = doc.getElementsByName && - // buggy browsers will return fewer than the correct 2 - doc.getElementsByName( expando ).length === 2 + - // buggy browsers will return more than the correct 0 - doc.getElementsByName( expando + 0 ).length; - - // Cleanup - docElem.removeChild( div ); - - return pass; - }); - - // Support: Webkit<537.32 - // Detached nodes confoundingly follow *each other* - support.sortDetached = assert(function( div1 ) { - return div1.compareDocumentPosition && - // Should return 1, but Webkit returns 4 (following) - (div1.compareDocumentPosition( document.createElement("div") ) & 1); - }); - - // IE6/7 return modified attributes - Expr.attrHandle = assert(function( div ) { - div.innerHTML = ""; - return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && - div.firstChild.getAttribute("href") === "#"; - }) ? - {} : - { - "href": function( elem ) { - return elem.getAttribute( "href", 2 ); - }, - "type": function( elem ) { - return elem.getAttribute("type"); - } - }; - - // ID find and filter - if ( support.getByName ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && !documentIsXML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && !documentIsXML ) { - var m = context.getElementById( id ); - - return m ? - m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? - [m] : - undefined : - []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.tagNameNoComments ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Name - Expr.find["NAME"] = support.getByName && function( tag, context ) { - if ( typeof context.getElementsByName !== strundefined ) { - return context.getElementsByName( name ); - } - }; - - // Class - Expr.find["CLASS"] = support.getByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) { - return context.getElementsByClassName( className ); - } - }; - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21), - // no need to also add to buggyMatches since matches checks buggyQSA - // A support test would require too much code (would include document ready) - rbuggyQSA = [ ":focus" ]; - - if ( (support.qsa = isNative(doc.querySelectorAll)) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explictly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // IE8 - Some boolean attributes are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - - // Opera 10-12/IE8 - ^= $= *= and empty values - // Should not select anything - div.innerHTML = ""; - if ( div.querySelectorAll("[i^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector || - docElem.mozMatchesSelector || - docElem.webkitMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - // Document order sorting - sortOrder = docElem.compareDocumentPosition ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); - - if ( compare ) { - // Disconnected nodes - if ( compare & 1 || - (recompare && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } - - // Not directly comparable, sort on existence of method - return a.compareDocumentPosition ? -1 : 1; - } : - function( a, b ) { - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Parentless nodes are either documents or disconnected - } else if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return document; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - // rbuggyQSA always contains :focus, so no need for an existence check - if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) { - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [elem] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - var val; - - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - if ( !documentIsXML ) { - name = name.toLowerCase(); - } - if ( (val = Expr.attrHandle[ name ]) ) { - return val( elem ); - } - if ( documentIsXML || support.attributes ) { - return elem.getAttribute( name ); - } - return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ? - name : - val && val.specified ? val.value : null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -// Document sorting and removing duplicates -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - // Compensate for sort limitations - recompare = !support.sortDetached; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - return results; -}; - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns Returns -1 if a precedes b, 1 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -// Returns a function to use in pseudos for input types -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -// Returns a function to use in pseudos for buttons -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -// Returns a function to use in pseudos for positionals -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[5] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[4] ) { - match[2] = match[4]; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeName ) { - if ( nodeName === "*" ) { - return function() { return true; }; - } - - nodeName = nodeName.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifider - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsXML ? - elem.getAttribute("xml:lang") || elem.getAttribute("lang") : - elem.lang) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( tokens = [] ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push( { - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - } ); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push( { - value: matched, - type: type, - matches: match - } ); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -} - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var data, cache, outerCache, - dirkey = dirruns + " " + doneName; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { - if ( (data = cache[1]) === true || data === cachedruns ) { - return data === true; - } - } else { - cache = outerCache[ dir ] = [ dirkey ]; - cache[1] = matcher( elem, context, xml ) || cachedruns; - if ( cache[1] === true ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - // A counter to specify which element is currently being matched - var matcherCachedRuns = 0, - bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { - var elem, j, matcher, - setMatched = [], - matchedCount = 0, - i = "0", - unmatched = seed && [], - outermost = expandContext != null, - contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); - - if ( outermost ) { - outermostContext = context !== document && context; - cachedruns = matcherCachedRuns; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - for ( ; (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - cachedruns = ++matcherCachedRuns; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); - } - i = group.length; - while ( i-- ) { - cached = matcherFromTokens( group[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - } - return cached; -}; - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function select( selector, context, results, seed ) { - var i, tokens, token, type, find, - match = tokenize( selector ); - - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - context.nodeType === 9 && !documentIsXML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && context.parentNode || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - } - - // Compile and execute a filtering function - // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( - seed, - context, - documentIsXML, - results, - rsibling.test( selector ) - ); - return results; -} - -// Deprecated -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -// Check sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Initialize with the default document -setDocument(); - -// Always assume the presence of duplicates if sort doesn't -// pass them to our comparison function (as in Google Chrome). -[0, 0].sort( sortOrder ); -support.detectDuplicates = hasDuplicate; - -// Override sizzle attribute retrieval -Sizzle.attr = jQuery.attr; -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})( window ); -var runtil = /Until$/, - rparentsprev = /^(?:parents|prev(?:Until|All))/, - isSimple = /^.[^:#\[\.,]*$/, - rneedsContext = jQuery.expr.match.needsContext, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var self, matched, i, - l = this.length; - - if ( typeof selector !== "string" ) { - self = this; - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - matched = []; - for ( i = 0; i < l; i++ ) { - jQuery.find( selector, this[ i ], matched ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - matched = this.pushStack( l > 1 ? jQuery.unique( matched ) : matched ); - matched.selector = ( this.selector ? this.selector + " " : "" ) + selector; - return matched; - }, - - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter(function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false) ); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true) ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - rneedsContext.test( selector ) ? - jQuery( selector, this.context ).index( this[ 0 ] ) >= 0 : - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - cur = matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return core_indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return core_indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( jQuery.unique(all) ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} - - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector( elems[ 0 ], expr ) ? [ elems[ 0 ] ] : [] : - jQuery.find.matches( expr, elems ); - }, - - dir: function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - var filtered; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - } - - if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem ) { - return ( elem === qualifier ) === keep; - }); - } - - if ( typeof qualifier === "string" ) { - filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, filtered, !keep ); - } - - qualifier = jQuery.filter( qualifier, filtered ); - } - - return jQuery.grep(elements, function( elem ) { - return ( core_indexOf.call( qualifier, elem ) >= 0 ) === keep; - }); -} -var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rhtml = /<|&#?\w+;/, - rnoInnerhtml = /<(?:script|style|link)/i, - manipulation_rcheckableType = /^(?:checkbox|radio)$/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /^$|\/(?:java|ecma)script/i, - rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - - // Support: IE 9 - option: [ 1, "" ], - - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] - }; - -// Support: IE 9 -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.col = wrapMap.thead; -wrapMap.th = wrapMap.td; - -jQuery.fn.extend({ - text: function( value ) { - return jQuery.access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, - - wrapAll: function( html ) { - var wrap; - - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapAll( html.call(this, i) ); - }); - } - - if ( this[ 0 ] ) { - - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); - - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); - } - - wrap.map(function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; - } - - return elem; - }).append( this ); - } - - return this; - }, - - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapInner( html.call(this, i) ); - }); - } - - return this.each(function() { - var self = jQuery( this ), - contents = self.contents(); - - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - }); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each(function( i ) { - jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - }, - - append: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.insertBefore( elem, this.firstChild ); - } - }); - }, - - before: function() { - return this.domManip(arguments, false, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip(arguments, false, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - var elem, - i = 0, - l = this.length; - - for ( ; i < l; i++ ) { - elem = this[ i ]; - - if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0, - l = this.length; - - for ( ; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function () { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return jQuery.access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function( value ) { - var isFunction = jQuery.isFunction( value ); - - // Make sure that the elements are removed from the DOM before they are inserted - // this can help fix replacing a parent with child elements - if ( !isFunction && typeof value !== "string" ) { - value = jQuery( value ).not( this ).detach(); - } - - return value !== "" ? - this.domManip( [ value ], true, function( elem ) { - var next = this.nextSibling, - parent = this.parentNode; - - if ( parent ) { - jQuery( this ).remove(); - parent.insertBefore( elem, next ); - } - }) : - this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, table, callback ) { - - // Flatten any nested arrays - args = core_concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, table ? self.html() : undefined ); - } - self.domManip( args, table, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - table = table && jQuery.nodeName( first, "tr" ); - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( - table && jQuery.nodeName( this[ i ], "table" ) ? - findOrAppend( this[ i ], "tbody" ) : - this[ i ], - node, - i - ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Hope ajax is available... - jQuery.ajax({ - url: node.src, - type: "GET", - dataType: "script", - async: false, - global: false, - "throws": true - }); - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: QtWebKit - // .get() because core_push.apply(_, arraylike) throws - core_push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Support: IE >=9 - // Fix Cloning issues - if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var elem, tmp, tag, wrap, contains, j, - i = 0, - l = elems.length, - fragment = context.createDocumentFragment(), - nodes = []; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.firstChild; - } - - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Fixes #12346 - // Support: Webkit, IE - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - }, - - cleanData: function( elems, /* internal */ acceptData ) { - var data, elem, type, - l = elems.length, - i = 0, - special = jQuery.event.special; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( acceptData || jQuery.acceptData( elem ) ) { - - data = data_priv.access( elem ); - - if ( data ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - } - // Discard any remaining `private` and `user` data - data_discard( elem ); - } - } -}); - -function findOrAppend( elem, tag ) { - return elem.getElementsByTagName( tag )[ 0 ] || elem.appendChild( elem.ownerDocument.createElement(tag) ); -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - var attr = elem.getAttributeNode("type"); - elem.type = ( attr && attr.specified ) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var l = elems.length, - i = 0; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = jQuery.extend( {}, pdataOld ); - events = pdataOld.events; - - data_priv.set( dest, pdataCur ); - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Support: IE >= 9 -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} -var curCSS, iframe, - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), - elemdisplay = { BODY: "block" }, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 - }, - - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; - -// return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { - - // shortcut for names that are not vendor prefixed - if ( name in style ) { - return name; - } - - // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in style ) { - return name; - } - } - - return origName; -} - -function isHidden( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); -} - -// NOTE: we've included the "window" in window.getComputedStyle -// because jsdom on node.js will break without it. -function getStyles( elem ) { - return window.getComputedStyle( elem, null ); -} - -function showHide( elements, show ) { - var display, elem, hidden, - values = [], - index = 0, - length = elements.length; - - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - values[ index ] = jQuery._data( elem, "olddisplay" ); - display = elem.style.display; - if ( show ) { - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !values[ index ] && display === "none" ) { - elem.style.display = ""; - } - - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); - } - } else { - - if ( !values[ index ] ) { - hidden = isHidden( elem ); - - if ( display && display !== "none" || !hidden ) { - jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); - } - } - } - } - - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( index = 0; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - if ( !show || elem.style.display === "none" || elem.style.display === "" ) { - elem.style.display = show ? values[ index ] || "" : "none"; - } - } - - return elements; -} - -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; - - if ( jQuery.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; - - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } - - return map; - } - - return value !== undefined ? - jQuery.style( elem, name, value ) : - jQuery.css( elem, name ); - }, name, value, arguments.length > 1 ); - }, - show: function() { - return showHide( this, true ); - }, - hide: function() { - return showHide( this ); - }, - toggle: function( state ) { - var bool = typeof state === "boolean"; - - return this.each(function() { - if ( bool ? state : isHidden( this ) ) { - jQuery( this ).show(); - } else { - jQuery( this ).hide(); - } - }); - } -}); - -jQuery.extend({ - // Add in style property hooks for overriding the default - // behavior of getting and setting a style property - cssHooks: { - opacity: { - get: function( elem, computed ) { - if ( computed ) { - // We should always get a number back from opacity - var ret = curCSS( elem, "opacity" ); - return ret === "" ? "1" : ret; - } - } - } - }, - - // Exclude the following css properties to add px - cssNumber: { - "columnCount": true, - "fillOpacity": true, - "fontWeight": true, - "lineHeight": true, - "opacity": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true - }, - - // Add in properties whose names you wish to fix before - // setting or getting the value - cssProps: { - // normalize float css property - "float": "cssFloat" - }, - - // Get and set the style property on a DOM Node - style: function( elem, name, value, extra ) { - // Don't set styles on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { - return; - } - - // Make sure that we're working with the right name - var ret, type, hooks, - origName = jQuery.camelCase( name ), - style = elem.style; - - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // Check if we're setting a value - if ( value !== undefined ) { - type = typeof value; - - // convert relative number strings (+= or -=) to relative numbers. #7345 - if ( type === "string" && (ret = rrelNum.exec( value )) ) { - value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); - // Fixes bug #9237 - type = "number"; - } - - // Make sure that NaN and null values aren't set. See: #7116 - if ( value == null || type === "number" && isNaN( value ) ) { - return; - } - - // If a number was passed in, add 'px' to the (except for certain CSS properties) - if ( type === "number" && !jQuery.cssNumber[ origName ] ) { - value += "px"; - } - - // Fixes #8908, it can be done more correctly by specifying setters in cssHooks, - // but it would mean to define eight (for every problematic property) identical functions - if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { - style[ name ] = "inherit"; - } - - // If a hook was provided, use that value, otherwise just set the specified value - if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { - style[ name ] = value; - } - - } else { - // If a hook was provided get the non-computed value from there - if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { - return ret; - } - - // Otherwise just get the value from the style object - return style[ name ]; - } - }, - - css: function( elem, name, extra, styles ) { - var val, num, hooks, - origName = jQuery.camelCase( name ); - - // Make sure that we're working with the right name - name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); - - // gets hook for the prefixed version - // followed by the unprefixed version - hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; - - // If a hook was provided get the computed value from there - if ( hooks && "get" in hooks ) { - val = hooks.get( elem, true, extra ); - } - - // Otherwise, if a way to get the computed value exists, use that - if ( val === undefined ) { - val = curCSS( elem, name, styles ); - } - - //convert "normal" to computed value - if ( val === "normal" && name in cssNormalTransform ) { - val = cssNormalTransform[ name ]; - } - - // Return, converting to number if forced or a qualifier was provided and val looks numeric - if ( extra === "" || extra ) { - num = parseFloat( val ); - return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; - } - return val; - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -curCSS = function( elem, name, _computed ) { - var width, minWidth, maxWidth, - computed = _computed || getStyles( elem ), - - // Support: IE9 - // getPropertyValue is only needed for .css('filter') in IE9, see #12537 - ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, - style = elem.style; - - if ( computed ) { - - if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { - ret = jQuery.style( elem, name ); - } - - // Support: Chrome <17, Safari 5.1 - // A tribute to the "awesome hack by Dean Edwards" - // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right - // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels - // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values - if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { - - // Remember the original values - width = style.width; - minWidth = style.minWidth; - maxWidth = style.maxWidth; - - // Put in the new values to get a computed value out - style.minWidth = style.maxWidth = style.width = ret; - ret = computed.width; - - // Revert the changed values - style.width = width; - style.minWidth = minWidth; - style.maxWidth = maxWidth; - } - } - - return ret; -}; - - -function setPositiveNumber( elem, value, subtract ) { - var matches = rnumsplit.exec( value ); - return matches ? - // Guard against undefined "subtract", e.g., when used as in cssHooks - Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : - value; -} - -function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { - var i = extra === ( isBorderBox ? "border" : "content" ) ? - // If we already have the right measurement, avoid augmentation - 4 : - // Otherwise initialize for horizontal or vertical properties - name === "width" ? 1 : 0, - - val = 0; - - for ( ; i < 4; i += 2 ) { - // both box models exclude margin, so add it if we want it - if ( extra === "margin" ) { - val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); - } - - if ( isBorderBox ) { - // border-box includes padding, so remove it if we want content - if ( extra === "content" ) { - val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - } - - // at this point, extra isn't border nor margin, so remove border - if ( extra !== "margin" ) { - val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } else { - // at this point, extra isn't content, so add padding - val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); - - // at this point, extra isn't content nor padding, so add border - if ( extra !== "padding" ) { - val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); - } - } - } - - return val; -} - -function getWidthOrHeight( elem, name, extra ) { - - // Start with offset property, which is equivalent to the border-box value - var valueIsBorderBox = true, - val = name === "width" ? elem.offsetWidth : elem.offsetHeight, - styles = getStyles( elem ), - isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; - - // some non-html elements return undefined for offsetWidth, so check for null/undefined - // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 - // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 - if ( val <= 0 || val == null ) { - // Fall back to computed then uncomputed css if necessary - val = curCSS( elem, name, styles ); - if ( val < 0 || val == null ) { - val = elem.style[ name ]; - } - - // Computed unit is not pixels. Stop here and return. - if ( rnumnonpx.test(val) ) { - return val; - } - - // we need the check for style in case a browser which returns unreliable values - // for getComputedStyle silently falls back to the reliable elem.style - valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); - - // Normalize "", auto, and prepare for extra - val = parseFloat( val ) || 0; - } - - // use the active box-sizing model to add/subtract irrelevant styles - return ( val + - augmentWidthOrHeight( - elem, - name, - extra || ( isBorderBox ? "border" : "content" ), - valueIsBorderBox, - styles - ) - ) + "px"; -} - -// Try to determine the default display value of an element -function css_defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - // Use the already-created iframe if possible - iframe = ( iframe || - jQuery(" -
    -
    -
    -
    -
    - - - - - - - - - - - - - - - {{> ipAddress }} - {{> patternPaths }} - {{> viewAllPaths }} - - - - - - - {{> websockets }} - - - \ No newline at end of file diff --git a/core/templates/partials/ipAddress.mustache b/core/templates/partials/ipAddress.mustache deleted file mode 100644 index b62f309ce..000000000 --- a/core/templates/partials/ipAddress.mustache +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/core/templates/partials/ishControls.mustache b/core/templates/partials/ishControls.mustache deleted file mode 100644 index 2dd6b2e9b..000000000 --- a/core/templates/partials/ishControls.mustache +++ /dev/null @@ -1,66 +0,0 @@ - -
    -
    -
      -
    • -
      -
      - Size px / - em -
      -
      -
        - {{^ ishControlsHide.s }}
      • S
      • {{/ ishControlsHide.s }} - {{^ ishControlsHide.m }}
      • M
      • {{/ ishControlsHide.m }} - {{^ ishControlsHide.l }}
      • L
      • {{/ ishControlsHide.l }} - {{^ ishControlsHide.full }}
      • Full
      • {{/ ishControlsHide.full }} - {{^ ishControlsHide.random }}
      • Random
      • {{/ ishControlsHide.random }} - {{^ ishControlsHide.disco }}
      • Disco
      • {{/ ishControlsHide.disco }} - {{^ ishControlsHide.hay }}
      • Hay!
      • {{/ ishControlsHide.hay }} -
      -
    • - {{^ ishControlsHide.mqs }} -
    • - MQ -
        - {{# mqs }} -
      • {{ . }}
      • - {{/ mqs }} -
      -
    • - {{/ ishControlsHide.mqs }} - {{^ ishControlsHide.find }} -
    • - Search Patterns -
        -
      • -
      -
    • - {{/ ishControlsHide.find }} - {{^ ishControlsHide.views-all }} -
    • - View -
        - {{^ ishControlsHide.views-annotations }}
      • Annotations
      • {{/ ishControlsHide.views-annotations }} - {{^ ishControlsHide.views-code }}
      • Code
      • {{/ ishControlsHide.views-code }} - {{^ ishControlsHide.views-new }}
      • Open in new window
      • {{/ ishControlsHide.views-new }} -
      -
    • - {{/ ishControlsHide.views-all }} - {{^ ishControlsHide.tools-all }} -
    • - Tools -
        - {{^ ishControlsHide.tools-follow }}
      • Page Follow
      • {{/ ishControlsHide.tools-follow }} - {{^ ishControlsHide.tools-reload }}
      • Auto-reload
      • {{/ ishControlsHide.tools-reload }} - {{^ ishControlsHide.tools-snapshot }}
      • Snapshots
      • {{/ ishControlsHide.tools-snapshot }} - {{^ ishControlsHide.tools-shortcuts }}
      • Keyboard Shortcuts{{/ ishControlsHide.tools-shortcuts }} - {{^ ishControlsHide.tools-docs }}
      • Pattern Lab Docs{{/ ishControlsHide.tools-docs }} -
      -
    • - {{/ ishControlsHide.tools-all }} -
    - -
    -
    - \ No newline at end of file diff --git a/core/templates/partials/patternNav.mustache b/core/templates/partials/patternNav.mustache deleted file mode 100644 index ae337de31..000000000 --- a/core/templates/partials/patternNav.mustache +++ /dev/null @@ -1,17 +0,0 @@ -
      - {{# patternTypes }} -
    1. {{ patternTypeUC }}
        - {{# patternTypeItems }} -
      1. {{ patternSubtypeUC }}
          - {{# patternSubtypeItems }} -
        1. {{ patternName }}
        2. - {{/ patternSubtypeItems }} -
      2. - {{/ patternTypeItems }} - {{# patternItems }} -
      3. {{ patternName }}
      4. - {{/ patternItems }} -
    2. - {{/ patternTypes }} -
    3. All
    4. -
    diff --git a/core/templates/partials/patternPaths.mustache b/core/templates/partials/patternPaths.mustache deleted file mode 100644 index 3b8562337..000000000 --- a/core/templates/partials/patternPaths.mustache +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/core/templates/partials/patternSection.mustache b/core/templates/partials/patternSection.mustache deleted file mode 100644 index b7033459e..000000000 --- a/core/templates/partials/patternSection.mustache +++ /dev/null @@ -1,91 +0,0 @@ -
    -
    -

    {{ patternName }}

    -
    - Pattern header options -
    -
    - {{# patternDescExists }} -
    - {{{ patternDesc }}} - {{# patternModifiersExist }} -
      - {{# patternModifiers }} -
    • {{ modifierName }}: {{ modifierDesc }}
    • - {{/ patternModifiers }} -
    - {{/ patternModifiersExist }} -
    - {{/ patternDescExists }} -
    - {{{ patternPartialCode }}} -
    - {{# patternModifiersExist }} - {{# patternModifiers }} - {{# modifierCodeExists }} -
    - {{ modifierName }} - {{{ modifierCode }}} -
    - {{/ modifierCodeExists }} - {{/ patternModifiers }} - {{/ patternModifiersExist }} -
    - - - - - {{# patternCSSExists }} - - {{/ patternCSSExists }} -
    -
    \ No newline at end of file diff --git a/core/templates/partials/patternSectionSubtype.mustache b/core/templates/partials/patternSectionSubtype.mustache deleted file mode 100644 index 56fa1b855..000000000 --- a/core/templates/partials/patternSectionSubtype.mustache +++ /dev/null @@ -1,6 +0,0 @@ -
    -

    {{ patternName }}

    -
    - {{{ patternDesc }}} -
    -
    \ No newline at end of file diff --git a/core/templates/partials/viewAllPaths.mustache b/core/templates/partials/viewAllPaths.mustache deleted file mode 100644 index 885dc59e1..000000000 --- a/core/templates/partials/viewAllPaths.mustache +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/core/templates/partials/viewerAnnotation.mustache b/core/templates/partials/viewerAnnotation.mustache deleted file mode 100644 index 2f215f8eb..000000000 --- a/core/templates/partials/viewerAnnotation.mustache +++ /dev/null @@ -1,4 +0,0 @@ -
    - Close -
    -
    \ No newline at end of file diff --git a/core/templates/partials/viewerCode.mustache b/core/templates/partials/viewerCode.mustache deleted file mode 100644 index 8d2273b47..000000000 --- a/core/templates/partials/viewerCode.mustache +++ /dev/null @@ -1,32 +0,0 @@ -
    - Close -
    -
    -
    - Loading pattern... -
    - - - -
    -
      -
    • HTML
    • -
    • Mustache
    • - -
    -
    -
    -
    -
    \ No newline at end of file diff --git a/core/templates/partials/websockets.mustache b/core/templates/partials/websockets.mustache deleted file mode 100644 index 65af3be1c..000000000 --- a/core/templates/partials/websockets.mustache +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/core/templates/pattern-header-footer/README b/core/templates/pattern-header-footer/README deleted file mode 100644 index 6c5a981ab..000000000 --- a/core/templates/pattern-header-footer/README +++ /dev/null @@ -1 +0,0 @@ -This is not a real pattern. It's simply the header and footer that patterns get sandwiched between when they're processed by the builder. \ No newline at end of file diff --git a/core/templates/pattern-header-footer/footer-pattern.html b/core/templates/pattern-header-footer/footer-pattern.html deleted file mode 100644 index 772e94e13..000000000 --- a/core/templates/pattern-header-footer/footer-pattern.html +++ /dev/null @@ -1,15 +0,0 @@ - - - \ No newline at end of file diff --git a/core/templates/pattern-header-footer/footer.html b/core/templates/pattern-header-footer/footer.html deleted file mode 100644 index 64db6455a..000000000 --- a/core/templates/pattern-header-footer/footer.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - diff --git a/core/templates/pattern-header-footer/header.html b/core/templates/pattern-header-footer/header.html deleted file mode 100644 index ec9904f70..000000000 --- a/core/templates/pattern-header-footer/header.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/core/templates/snapshot.mustache b/core/templates/snapshot.mustache deleted file mode 100644 index 25e428a78..000000000 --- a/core/templates/snapshot.mustache +++ /dev/null @@ -1,17 +0,0 @@ - - -
    -

    Snapshots

    -

    These are a list of the snapshots you've made of your Pattern Lab project:

    -
      - {{{ html }}} -
    -
    - - - - diff --git a/core/templates/viewall.mustache b/core/templates/viewall.mustache deleted file mode 100644 index 8daaf4460..000000000 --- a/core/templates/viewall.mustache +++ /dev/null @@ -1,25 +0,0 @@ - - -
    - - -
    - {{# partials }} - {{# patternSectionSubtype }} - {{> patternSectionSubtype }} - {{/ patternSectionSubtype }} - {{^ patternSectionSubtype }} - {{> patternSection }} - {{/ patternSectionSubtype }} - {{/ partials }} -
    - -
    - - - - diff --git a/extras/apache/README b/extras/apache/README deleted file mode 100644 index 4a58f3819..000000000 --- a/extras/apache/README +++ /dev/null @@ -1,37 +0,0 @@ -# How to Set-up Apache on Mac OS X - -This document reviews how to (hopefully) easily set-up Apache on Mac OS X to support Pattern Lab. You'll need to open Terminal. Note that PHP may flake out with the default Apache install. I can modify directions in the future to account for that. - -## 1. Modify hosts - -First, you'll want to modify your hosts file so that you can use a specific hostname for the site rather than just `127.0.0.1` or `localhost`. To do so do the following: - -1. In Terminal type `sudo vi /etc/hosts` -2. When prompted, enter the password you use to log-in -3. When the file loads type `i` -4. Using the arrow keys get to the end of the last line -5. Hit `return` and type `127.0.0.1 patternlab.localhost` -6. Hit the `esc` key and type `:wq` - -Your hosts should now be saved. - -## 2. Modify Apache - -Second, you'll want to add an Apache `VirtualHost` so that Apache will know to listen for your application at the correct hostname. - -1. In Terminal type `sudo vi /etc/apache2/extra/httpd-vhosts.conf` -2. When prompted, enter the password you use to log-in -3. When the file loads type `i` -4. Using the arrow keys get to the end of the last line -5. Hit `return` twice -6. Copy and paste the info from the `vhost.txt` file in this directory. -7. Modify `DocumentRoot` path to match the location of the your install of Pattern Lab -7. Hit the `esc` key and type `:wq` - -## 3. Restart Apache - -Last, you'll want to restart Apache so your changes take effect. Simply open System Preferences and go to the "Sharing" panel. Untick the "Web Sharing" checkbox and tick it again to restart Apache. - -## 4. Test By Visiting patternlab.localhost - -In a browser try to visit http://patternlab.localhost. You should get the Pattern Lab styleguide by default. If you get Google Search results just make sure you enter the http:// \ No newline at end of file diff --git a/extras/apache/vhost.txt b/extras/apache/vhost.txt deleted file mode 100644 index e4431fc02..000000000 --- a/extras/apache/vhost.txt +++ /dev/null @@ -1,5 +0,0 @@ - - DocumentRoot "/Users/dmolsen/Sites/patternlab/public" - ServerName patternlab.localhost - ServerAlias patternlab.*.xip.io - \ No newline at end of file From b5769cc6673a91ce029d146e4aa169594af830ce Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Mon, 11 Jul 2016 23:01:40 -0400 Subject: [PATCH 156/166] laying in Pattern Lab 2 --- .gitignore | 8 +- LICENSE | 2 +- composer.json | 66 ++++++++++++ config/{README => .gitkeep} | 0 core/console | 46 +++++++++ core/server/router.php | 41 ++++++++ core/src/PatternLab/Installer.php | 162 ++++++++++++++++++++++++++++++ public/README | 3 - source/{README => .gitkeep} | 0 9 files changed, 319 insertions(+), 9 deletions(-) create mode 100644 composer.json rename config/{README => .gitkeep} (100%) create mode 100644 core/console create mode 100644 core/server/router.php create mode 100644 core/src/PatternLab/Installer.php delete mode 100644 public/README rename source/{README => .gitkeep} (100%) diff --git a/.gitignore b/.gitignore index cfc3838ca..d4f687c7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ .DS_Store +export/* public/* -source/* -config.ini -latest-change.txt -*-ck.js -*.sass-cache/ \ No newline at end of file +source/styleguide/* +vendor/* diff --git a/LICENSE b/LICENSE index 465444edd..1190640f9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-14 Brad Frost, http://bradfrostweb.com & Dave Olsen, http://dmolsen.com +Copyright (c) 2016 Brad Frost, http://bradfrostweb.com & Dave Olsen, http://dmolsen.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..d0049e01e --- /dev/null +++ b/composer.json @@ -0,0 +1,66 @@ +{ + "name": "pattern-lab/edition-mustache-standard", + "description": "The Standard Edition of Pattern Lab for Mustache. Installs all Mustache-related dependencies.", + "keywords": ["pattern lab", "mustache"], + "homepage": "http://patternlab.io", + "license": "MIT", + "type": "project", + "authors": [ + { + "name": "Dave Olsen", + "email": "dmolsen@gmail.com", + "homepage": "http://dmolsen.com", + "role": "Lead Developer" + } + ], + "support": { + "issues": "https://github.com/pattern-lab/patternlab-php/issues", + "wiki": "http://patternlab.io/docs/", + "source": "https://github.com/pattern-lab/patternlab-php/releases" + }, + "autoload": { + "psr-0": { + "PatternLab": "core/src/" + } + }, + "require": { + "php": ">=5.3.6", + "pattern-lab/core": "^2.0.0", + "pattern-lab/patternengine-mustache": "^2.0.0", + "pattern-lab/styleguidekit-mustache-default": "^3.0.0" + }, + "scripts": { + "server": "php core/console --server", + "generate": "php core/console --generate", + "watch": "php core/console --watch", + "start": "php core/console --server --with-watch", + "post-install-cmd": [ + "PatternLab\\Installer::postInstallCmd" + ], + "post-update-cmd": [ + "PatternLab\\Installer::postUpdateCmd" + ], + "post-root-package-install": [ + "PatternLab\\Installer::setProjectInstall", + "PatternLab\\Installer::getSuggestedStarterKits", + "PatternLab\\Installer::getConfigOverrides" + ], + "post-package-install": [ + "PatternLab\\Installer::postPackageInstall" + ], + "post-package-update": [ + "PatternLab\\Installer::postPackageUpdate" + ], + "pre-package-uninstall": [ + "PatternLab\\Installer::prePackageUninstall" + ] + }, + "extra": { + "patternlab": { + "starterKitSuggestions": [ + "pattern-lab/starterkit-mustache-demo", + "pattern-lab/starterkit-mustache-base" + ] + } + } +} diff --git a/config/README b/config/.gitkeep similarity index 100% rename from config/README rename to config/.gitkeep diff --git a/core/console b/core/console new file mode 100644 index 000000000..51e4c0d14 --- /dev/null +++ b/core/console @@ -0,0 +1,46 @@ +dispatch("config.configLoadEnd"); + +// run the console +Console::run(); diff --git a/core/server/router.php b/core/server/router.php new file mode 100644 index 000000000..02bc4c232 --- /dev/null +++ b/core/server/router.php @@ -0,0 +1,41 @@ + false, "packagesRemove" => false, "suggestedStarterKits" => array(), "configOverrides" => array(), "patternLabPackages" => array()); + + /** + * Get any config overrides that may exist for the edition + * @param {Object} a script event object from composer + */ + public static function getConfigOverrides(Event $event) { + + $extra = $event->getComposer()->getPackage()->getExtra(); + if (isset($extra["patternlab"]) && isset($extra["patternlab"]["config"]) && is_array($extra["patternlab"]["config"])) { + self::$installerInfo["configOverrides"] = $extra["patternlab"]["config"]; + } + + } + + /** + * Get the package info from each patternlab-* package's composer.json + * @param {String} the type of event fired during the composer install + * @param {Object} a script event object from composer + */ + public static function getPackageInfo($type, $event) { + + $package = ($type == "update") ? $event->getOperation()->getTargetPackage() : $event->getOperation()->getPackage(); + $packageType = $package->getType(); + $packageExtra = $package->getExtra(); + $packageInfo = array(); + + // make sure we're only evaluating pattern lab packages + if (strpos($packageType,"patternlab-") !== false) { + + $packageInfo["name"] = $package->getName(); + $packageInfo["type"] = $packageType; + $packageInfo["pathBase"] = $event->getComposer()->getInstallationManager()->getInstallPath($package); + $packageInfo["pathDist"] = $packageInfo["pathBase"].DIRECTORY_SEPARATOR."dist".DIRECTORY_SEPARATOR; + $packageInfo["extra"] = (isset($packageExtra["patternlab"])) ? $packageExtra["patternlab"] : array(); + + self::$installerInfo["packages"][] = $packageInfo; + + } + + } + + /** + * Get the suggested starter kits from the root package composer.json + * @param {Object} a script event object from composer + */ + public static function getSuggestedStarterKits(Event $event) { + + $extra = $event->getComposer()->getPackage()->getExtra(); + if (isset($extra["patternlab"]) && isset($extra["patternlab"]["starterKitSuggestions"]) && is_array($extra["patternlab"]["starterKitSuggestions"])) { + self::$installerInfo["suggestedStarterKits"] = $extra["patternlab"]["starterKitSuggestions"]; + } + + } + + /** + * Run the centralized postInstallCmd + * @param {Object} a script event object from composer + */ + public static function postInstallCmd(Event $event) { + + InstallerUtil::postInstallCmd(self::$installerInfo, $event); + + } + + /** + * Run the centralized postUpdateCmd + * @param {Object} a script event object from composer + */ + public static function postUpdateCmd(Event $event) { + + InstallerUtil::postUpdateCmd(self::$installerInfo, $event); + + } + + /** + * Clean-up when a package is removed + * @param {Object} a script event object from composer + */ + public static function postPackageInstall(PackageEvent $event) { + + self::getPackageInfo("install", $event); + + } + + /** + * Clean-up when a package is removed + * @param {Object} a script event object from composer + */ + public static function postPackageUpdate(PackageEvent $event) { + + self::getPackageInfo("update", $event); + + } + + /** + * Clean-up when a package is removed + * @param {Object} a script event object from composer + */ + public static function prePackageUninstall(PackageEvent $event) { + + // make sure the postUpdateCmd doesnt actually do anything + self::setPackagesRemove(); + + // get the basic package info + $package = $event->getOperation()->getPackage(); + $packageType = $package->getType(); + $packageInfo = array(); + + // make sure we're only evaluating pattern lab packages. remove attributes related to them. + if (strpos($packageType,"patternlab-") !== false) { + + $packageInfo["name"] = $package->getName(); + $packageInfo["type"] = $packageType; + $packageInfo["pathBase"] = $event->getComposer()->getInstallationManager()->getInstallPath($package); + + InstallerUtil::packageRemove($packageInfo); + + } + + } + + /** + * Set the packages remove boolean to true + */ + public static function setPackagesRemove() { + + self::$installerInfo["packagesRemove"] = true; + + } + + /** + * Set the project install boolean to true + * @param {Object} a script event object from composer + */ + public static function setProjectInstall(Event $event) { + + self::$installerInfo["projectInstall"] = true; + + } + +} diff --git a/public/README b/public/README deleted file mode 100644 index 5c889f2d5..000000000 --- a/public/README +++ /dev/null @@ -1,3 +0,0 @@ -To populate this directory you need to generate the PHP version of Pattern Lab for the first time. -To do so either run "php core/builder.php -g" at the command line or, if you're on a Mac, double-click -on "core/scripts/generateSite.command". \ No newline at end of file diff --git a/source/README b/source/.gitkeep similarity index 100% rename from source/README rename to source/.gitkeep From 3d3ebc91c50569af2e740701f71bfa828b37bf00 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:05:55 -0400 Subject: [PATCH 157/166] updating the copy --- README.md | 129 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index ebcc7469c..efe58d7e7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ -## About Pattern Lab -- [Pattern Lab Website](http://patternlab.io/) -- [About Pattern Lab](http://patternlab.io/about.html) -- [Documentation](http://patternlab.io/docs/index.html) -- [Demo](http://demo.patternlab.io/) +![license](https://img.shields.io/github/license/pattern-lab/patternlab-php.svg) +[![Packagist](https://img.shields.io/packagist/v/pattern-lab/edition-mustache-standard.svg)](https://packagist.org/packages/pattern-lab/edition-mustache-standard) [![Gitter](https://img.shields.io/gitter/room/pattern-lab/php.svg)](https://gitter.im/pattern-lab/php) -The PHP version of Pattern Lab is, at its core, a static site generator. It combines platform-agnostic assets, like the [Mustache](http://mustache.github.io/)-based patterns and the JavaScript-based viewer, with a PHP-based "builder" that transforms and dynamically builds the Pattern Lab site. By making it a static site generator, Pattern Lab strongly separates patterns, data, and presentation from build logic. +# Pattern Lab Standard Edition for Mustache + +The Pattern Lab Standard Edition for Mustache is the evolution of Pattern Lab 1. Pattern Lab is still, at its core, a prototyping tool focused on encouraging communication between content creators, designers, devs, and clients. It combines platform-agnostic assets, like the [Mustache](http://mustache.github.io/)-based patterns, with a PHP-based "builder". Pattern Lab 2 introduces [the beginnings of an ecosystem](http://patternlab.io/docs/advanced-ecosystem-overview.html) that will allow teams to mix, match and extend Pattern Lab to meet their specific needs. It will also make it easier for the Pattern Lab team to push out new features. Pattern Lab Standard Edition for Mustache is just [one of four PHP-based Editions currently available](http://patternlab.io/docs/installation.html). ## Demo @@ -13,50 +12,74 @@ You can play with a demo of the front-end of Pattern Lab at [demo.patternlab.io] ## Getting Started * [Requirements](http://patternlab.io/docs/requirements.html) -* [Installing the PHP Version of Pattern Lab](http://patternlab.io/docs/installation.html) -* [Upgrading the PHP Version of Pattern Lab](http://patternlab.io/docs/upgrading.html) -* [Generating the Pattern Lab Website for the First Time](http://patternlab.io/docs/first-run.html) -* [Editing the Pattern Lab Website Source Files](http://patternlab.io/docs/editing-source-files.html) -* [Using the Command-line Options](http://patternlab.io/docs/command-line.html) -* [Command Prompt on Windows](http://patternlab.io/docs/command-prompt-windows.html) - -## Working with Patterns - -Patterns are the core element of Pattern Lab. Understanding how they work is the key to getting the most out of the system. Patterns use [Mustache](http://mustache.github.io/) so please read [Mustache's docs](http://mustache.github.io/mustache.5.html) as well. - -* [How Patterns Are Organized](http://patternlab.io/docs/pattern-organization.html) -* [Adding New Patterns](http://patternlab.io/docs/pattern-add-new.html) -* [Reorganizing Patterns](http://patternlab.io/docs/pattern-reorganizing.html) -* [Including One Pattern Within Another via Partials](http://patternlab.io/docs/pattern-including.html) -* [Managing Assets for a Pattern: JavaScript, images, CSS, etc.](http://patternlab.io/docs/pattern-managing-assets.html) -* [Modifying the Pattern Header and Footer](http://patternlab.io/docs/pattern-header-footer.html) -* [Using Pseudo-Patterns](http://patternlab.io/docs/pattern-pseudo-patterns.html) -* [Using Pattern Parameters](http://patternlab.io/docs/pattern-parameters.html) -* [Using Pattern State](http://patternlab.io/docs/pattern-states.html) -* ["Hiding" Patterns in the Navigation](http://patternlab.io/docs/pattern-hiding.html) -* [Adding Annotations](http://patternlab.io/docs/pattern-adding-annotations.html) -* [Viewing Patterns on a Mobile Device](http://patternlab.io/docs/pattern-mobile-view.html) - -## Creating & Working With Dynamic Data for a Pattern - -The PHP version of Pattern Lab utilizes Mustache as the template language for patterns. In addition to allowing for the [inclusion of one pattern within another](http://patternlab.io/docs/pattern-including.html) it also gives pattern developers the ability to include variables. This means that attributes like image sources can be centralized in one file for easy modification across one or more patterns. The PHP version of Pattern Lab uses a JSON file, `source/_data/data.json`, to centralize many of these attributes. - -* [Introduction to JSON & Mustache Variables](http://patternlab.io/docs/data-json-mustache.html) -* [Overriding the Central `data.json` Values with Pattern-specific Values](http://patternlab.io/docs/data-pattern-specific.html) -* [Linking to Patterns with Pattern Lab's Default `link` Variable](http://patternlab.io/docs/data-link-variable.html) -* [Creating Lists with Pattern Lab's Default `listItems` Variable](http://patternlab.io/docs/data-listitems.html) - -## Using Pattern Lab's Advanced Features - -By default, the Pattern Lab assets can be manually generated and the Pattern Lab site manually refreshed but who wants to waste time doing that? Here are some ways that Pattern Lab can make your development workflow a little smoother: - -* [Watching for Changes and Auto-Regenerating Patterns](http://patternlab.io/docs/advanced-auto-regenerate.html) -* [Auto-Reloading the Browser Window When Changes Are Made](http://patternlab.io/docs/advanced-reload-browser.html) -* [Multi-browser & Multi-device Testing with Page Follow](http://patternlab.io/docs/advanced-page-follow.html) -* [Keyboard Shortcuts](http://patternlab.io/docs/advanced-keyboard-shortcuts.html) -* [Special Pattern Lab-specific Query String Variables ](http://patternlab.io/docs/pattern-linking.html) -* [Preventing the Cleaning of public/](http://patternlab.io/docs/advanced-clean-public.html) -* [Generating CSS](http://patternlab.io/docs/advanced-generating-css.html) -* [Modifying the Pattern Lab Nav](http://patternlab.io/docs/advanced-pattern-lab-nav.html) -* [Editing the config.ini Options](http://patternlab.io/docs/advanced-config-options.html) -* [Integration with Compass](http://patternlab.io/docs/advanced-integration-with-compass.html) +* [Installing](#installing) +* [Generating](http://patternlab.io/docs/first-run.html) +* [Viewing](http://patternlab.io/docs/viewing-patterns.html) +* [Editing](http://patternlab.io/docs/editing-source-files.html) +* [Using commands](http://patternlab.io/docs/command-line.html) +* [Upgrading](http://patternlab.io/docs/upgrading.html) +* [More documentation](http://patternlab.io/docs/) + +## Installing + +There are two methods for downloading and installing the Standard Edition for Mustache: + +* Download a pre-built project +* Create a project based on this Edition with Composer + +### Download a pre-built project + +The fastest way to get started with Pattern Lab's Standard Edition for Mustache is to download the latest pre-built version from the [releases page](https://github.com/pattern-lab/patternlab-php/releases/latest). + +**Note:** You'll need to install [Composer](https://getcomposer.org/) in the future if you want to [upgrade Pattern Lab](http://patternlab.io/docs/upgrading.html). + +### Use Composer to create a project + +Pattern Lab uses [Composer](https://getcomposer.org/) to manage project dependencies. + +#### 1. Install Composer + +Please follow the directions for [installing Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx) on the Composer website. We recommend you [install it globally](https://getcomposer.org/doc/00-intro.md#globally). + +#### 2. Install the Standard Edition for Mustache + +Use Composer's [`create-project` command](https://getcomposer.org/doc/03-cli.md#create-project) to install the Standard Edition for Mustache into a location of your choosing. To create a project do the following: + +1. In a terminal window navigate to the root of your project +2. Type `composer create-project pattern-lab/edition-mustache-webdesignday your-project-name && cd $_` + +This will install the Standard Edition for Mustache into a directory called `your-project-name` in `install/location/`. You will be automatically dropped into the project directory after the process is finished. + +## Migrating from Pattern Lab 1 to Pattern Lab 2 + +Pattern Lab 2 was a complete rewrite and reorganization of Pattern Lab 1. After installing the Standard Edition for Mustache do the following to migrate from Pattern Lab 1 to Pattern Lab 2: + +1. Copy `./source` from your old project to your new install +2. Copy `./source/_patterns/00-atoms/00-meta/_00-head.mustache` to `./source/_meta/_00-head.mustache` +3. Copy `./source/_patterns/00-atoms/00-meta/_01-foot.mustache` to `./source/_meta/_00-foot.mustache` +4. Copy `./source/_data/annotations.js` to `./source/_annotations/annotations.js` + +Everything else should work without changes. + +## Need Pattern Lab 1? + +The [source code for Pattern Lab 1](https://github.com/pattern-lab/patternlab-php/releases/tag/v1.1.0) is still available for download. + +## Packaged Components + +The Standard Edition for Mustache installs the following components: + +* `pattern-lab/core`: [GitHub](https://github.com/pattern-lab/patternlab-php-core), [Packagist](https://packagist.org/packages/pattern-lab/core) +* `pattern-lab/patternengine-mustache`: [documentation](https://github.com/pattern-lab/patternengine-php-mustache#mustache-patternengine-for-pattern-lab-php), [GitHub](https://github.com/pattern-lab/patternengine-php-mustache), [Packagist](https://packagist.org/packages/pattern-lab/patternengine-mustache) +* `pattern-lab/styleguidekit-assets-default`: [GitHub](https://github.com/pattern-lab/styleguidekit-assets-default), [Packagist](https://packagist.org/packages/pattern-lab/styleguidekit-assets-default) +* `pattern-lab/styleguidekit-mustache-default`: [GitHub](https://github.com/pattern-lab/styleguidekit-mustache-default), [Packagist](https://packagist.org/packages/pattern-lab/styleguidekit-mustache-default) + +## List the Available Commands and Their Options + +To list all available commands type: + + php core/console --help + +To list the options for a particular command type: + + php core/console --help --[command] From 26a0a0056be8f83d52bf78b62b8d0815d48e9403 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:30:17 -0400 Subject: [PATCH 158/166] hopefully simplifying setup instructions --- README.md | 33 ++++++++++++++++++++++----------- composer.json | 3 ++- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index efe58d7e7..59c7dfea0 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,11 @@ The Pattern Lab Standard Edition for Mustache is the evolution of Pattern Lab 1. You can play with a demo of the front-end of Pattern Lab at [demo.patternlab.io](http://demo.patternlab.io). -## Getting Started +## Requirements -* [Requirements](http://patternlab.io/docs/requirements.html) -* [Installing](#installing) -* [Generating](http://patternlab.io/docs/first-run.html) -* [Viewing](http://patternlab.io/docs/viewing-patterns.html) -* [Editing](http://patternlab.io/docs/editing-source-files.html) -* [Using commands](http://patternlab.io/docs/command-line.html) -* [Upgrading](http://patternlab.io/docs/upgrading.html) -* [More documentation](http://patternlab.io/docs/) +To use the basic features of Pattern Lab to compile patterns, you must have **PHP 5.4+** installed. On Mac OS X Pattern Lab should work "out of the box." If you're on Windows you can [download PHP from PHP.net](http://windows.php.net/download/). Pattern Lab comes with its own built-in web server. + +Pattern Lab uses [Composer](https://getcomposer.org/) to manage project dependencies. It's required if you want to install Pattern Lab using Composer's `create-project` command or if you want to upgrade Pattern Lab in the future. ## Installing @@ -48,7 +43,22 @@ Use Composer's [`create-project` command](https://getcomposer.org/doc/03-cli.md# 1. In a terminal window navigate to the root of your project 2. Type `composer create-project pattern-lab/edition-mustache-webdesignday your-project-name && cd $_` -This will install the Standard Edition for Mustache into a directory called `your-project-name` in `install/location/`. You will be automatically dropped into the project directory after the process is finished. +This will install the Standard Edition for Mustache into a directory called `your-project-name` in `install/location/`. When prompted choose the "demo" StarterKit. You will be automatically dropped into the project directory after the process is finished. + +## Get Up and Running + +After installing do the following start Pattern Lab: + +1. In a terminal window navigate to the root of your project if you aren't there already +2. Type `php core/console --server --with-watch` + +You should now be able to open [http://localhost:8080](http://localhost:8080) to see your generated site. Any changes you make in `./source/` will automatically rebuild your site and reload your browser. + +As you get more comfortable with Pattern Lab you can [integrate it with a Gulp or Grunt workflow](http://patternlab.io/docs/advanced-integration-with-grunt.html) and drop some of the native Pattern Lab features like automatic browser reload. + +## More Documentation + +Obviously the reason to install Pattern Lab is more than the install process. [Check out the documentation](https://patternlab.io/docs/) to learn about how to use patterns, how to modify the data used to populate your patterns, and about some advanced features. ## Migrating from Pattern Lab 1 to Pattern Lab 2 @@ -71,10 +81,11 @@ The Standard Edition for Mustache installs the following components: * `pattern-lab/core`: [GitHub](https://github.com/pattern-lab/patternlab-php-core), [Packagist](https://packagist.org/packages/pattern-lab/core) * `pattern-lab/patternengine-mustache`: [documentation](https://github.com/pattern-lab/patternengine-php-mustache#mustache-patternengine-for-pattern-lab-php), [GitHub](https://github.com/pattern-lab/patternengine-php-mustache), [Packagist](https://packagist.org/packages/pattern-lab/patternengine-mustache) +* `pattern-lab/plugin-reload`: [GitHub](https://github.com/pattern-lab/plugin-php-reload), [Packagist](https://packagist.org/packages/pattern-lab/plugin-reload) * `pattern-lab/styleguidekit-assets-default`: [GitHub](https://github.com/pattern-lab/styleguidekit-assets-default), [Packagist](https://packagist.org/packages/pattern-lab/styleguidekit-assets-default) * `pattern-lab/styleguidekit-mustache-default`: [GitHub](https://github.com/pattern-lab/styleguidekit-mustache-default), [Packagist](https://packagist.org/packages/pattern-lab/styleguidekit-mustache-default) -## List the Available Commands and Their Options +## List All of the Available Commands and Their Options To list all available commands type: diff --git a/composer.json b/composer.json index d0049e01e..fbbf96141 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ "php": ">=5.3.6", "pattern-lab/core": "^2.0.0", "pattern-lab/patternengine-mustache": "^2.0.0", - "pattern-lab/styleguidekit-mustache-default": "^3.0.0" + "pattern-lab/styleguidekit-mustache-default": "^3.0.0", + "pattern-lab/plugin-reload": "^2.0.0" }, "scripts": { "server": "php core/console --server", From d8d6b6858a35cf619d9efb74fbeac90d65ef3ce4 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:34:02 -0400 Subject: [PATCH 159/166] more copy edits --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 59c7dfea0..94577c8ae 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Pattern Lab Standard Edition for Mustache -The Pattern Lab Standard Edition for Mustache is the evolution of Pattern Lab 1. Pattern Lab is still, at its core, a prototyping tool focused on encouraging communication between content creators, designers, devs, and clients. It combines platform-agnostic assets, like the [Mustache](http://mustache.github.io/)-based patterns, with a PHP-based "builder". Pattern Lab 2 introduces [the beginnings of an ecosystem](http://patternlab.io/docs/advanced-ecosystem-overview.html) that will allow teams to mix, match and extend Pattern Lab to meet their specific needs. It will also make it easier for the Pattern Lab team to push out new features. Pattern Lab Standard Edition for Mustache is just [one of four PHP-based Editions currently available](http://patternlab.io/docs/installation.html). +The Pattern Lab Standard Edition for Mustache is the evolution of Pattern Lab 1. Pattern Lab is still, at its core, a prototyping tool focused on encouraging communication between content creators, designers, devs, and clients. It combines platform-agnostic assets, like the [Mustache](http://mustache.github.io/)-based patterns, with a PHP-based "builder." Pattern Lab 2 introduces [the beginnings of an ecosystem](http://patternlab.io/docs/advanced-ecosystem-overview.html) that will allow teams to mix, match and extend Pattern Lab to meet their specific needs. It will also make it easier for the Pattern Lab team to push out new features. Pattern Lab Standard Edition for Mustache is just [one of the four PHP-based Editions currently available](http://patternlab.io/docs/installation.html). ## Demo @@ -26,8 +26,6 @@ There are two methods for downloading and installing the Standard Edition for Mu The fastest way to get started with Pattern Lab's Standard Edition for Mustache is to download the latest pre-built version from the [releases page](https://github.com/pattern-lab/patternlab-php/releases/latest). -**Note:** You'll need to install [Composer](https://getcomposer.org/) in the future if you want to [upgrade Pattern Lab](http://patternlab.io/docs/upgrading.html). - ### Use Composer to create a project Pattern Lab uses [Composer](https://getcomposer.org/) to manage project dependencies. @@ -47,7 +45,7 @@ This will install the Standard Edition for Mustache into a directory called `you ## Get Up and Running -After installing do the following start Pattern Lab: +After installing do the following to start and view Pattern Lab: 1. In a terminal window navigate to the root of your project if you aren't there already 2. Type `php core/console --server --with-watch` @@ -58,7 +56,7 @@ As you get more comfortable with Pattern Lab you can [integrate it with a Gulp o ## More Documentation -Obviously the reason to install Pattern Lab is more than the install process. [Check out the documentation](https://patternlab.io/docs/) to learn about how to use patterns, how to modify the data used to populate your patterns, and about some advanced features. +Obviously Pattern Lab is deeper than the install process. [Check out the documentation](https://patternlab.io/docs/) to learn about how to use patterns, how to modify the data used to populate those patterns, and about some advanced features. ## Migrating from Pattern Lab 1 to Pattern Lab 2 From 734645c07b8ab826ba258567d2ac2e4d4cdc9af3 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:36:31 -0400 Subject: [PATCH 160/166] updating the ignore so i can get public/.gitkeep saved --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index d4f687c7b..e3ee214fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .DS_Store export/* -public/* source/styleguide/* vendor/* From 16fd7d37252a2183878d047b8757eb6346127774 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:36:53 -0400 Subject: [PATCH 161/166] getting the .gitkeep file saved --- public/.gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/.gitkeep diff --git a/public/.gitkeep b/public/.gitkeep new file mode 100644 index 000000000..96c97655f --- /dev/null +++ b/public/.gitkeep @@ -0,0 +1 @@ +Holding file for public/ directory From 8e29330177c98e292be8f1e9d13feea2462368c8 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:37:22 -0400 Subject: [PATCH 162/166] re-adding public to the ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e3ee214fa..d4f687c7b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store export/* +public/* source/styleguide/* vendor/* From 970fbedcb4d1b5db4437f28f4621fe64ad1f4022 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 00:43:26 -0400 Subject: [PATCH 163/166] no longer applicable --- CHANGELOG | 205 ------------------------------------------------------ 1 file changed, 205 deletions(-) delete mode 100644 CHANGELOG diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index d79db913a..000000000 --- a/CHANGELOG +++ /dev/null @@ -1,205 +0,0 @@ -THIS CHANGELOG IS AN ATTEMPT TO DOCUMENT CHANGES TO THIS PROJECT. - -PL-v0.7.12 - - FIX: making sure only the hostname shows up for the websocket servers. no ports. - - THX: thanks to @levito for the pull request - -PL-v0.7.11 - - FIX: migrator now orders migrations properly for ubuntu - - THX: thanks to @krulik for reporting the issue & @paulovieira for confirming the fix - -PL-v0.7.10 - - ADD: more responsive pull bar - - THX: thanks to @crohrer for the pull request making the pull bar more responsive - -PL-v0.7.9 - - ADD: .sass-cache now ignored by default - - ADD: configuration options to disable loading of page follow & auto-reload - - ADD: configuration option to set cacheBuster to 0 - - ADD: configuration option to enable QR code feature - - ADD: configuration option to set minimum & maximum widths for the viewport resizer - - ADD: command line flag for generating only the patterns - - ADD: command line flag to set cacheBuster to 0 - - ADD: better handling of missing required directories in public/ - - ADD: added a styleguide-specific.css to better manage classes - - FIX: updated the console help output - - FIX: removed everything in public/ except the README - - FIX: removed the CDN version of jQuery - - FIX: toolbar styles updated - - FIX: icon font for the toolbar is no longer influencing the icon font of the patterns - - FIX: updated the styles for the "find a pattern" field - - FIX: code/annotations views now properly hide on resizing - - FIX: states.css removed and those styles loaded into styleguide.css - - FIX: default images updated - - FIX: removed hay mode from the tool bar by default. can be added back in by editing config.ini - - FIX: general pattern clean-up and re-styling to better match pattern lab's capabilities - - THX: thanks to @illepic for the .sass-cache PR - -PL-v0.7.8 - - ADD: can show annotations on view-all pages by default - - FIX: can target pattern divs and apply annotations - -PL-v0.7.7 - - ADD: can hide all of the patterns for a given pattern type from being shown on the styleguide. good for nested pages/templates - - FIX: the MQ menu is hidden on smaller viewports - -PL-v0.7.6 - - FIX: pattern search now searches the entire name of a pattern - - FIX: the MQ menu lines up with the navigation item - -PL-v0.7.5 - - ADD: a quick pattern search using typeahead - - ADD: keyboard shortcuts for a bunch of features using jwerty - - ADD: using cmd+a/ctrl+a when in code view selects the contents of the currently active tab - - ADD: code and annotation views can be opened automatically on load via query string params - - ADD: can use the config to remove items from pattern nav - - ADD: check for json support now that certain flavors of PHP 5.5.3 don't come with it - - ADD: can use boolean vars to enable/disable sections via pattern parameters - - ADD: pattern states for tracking progress on individual patterns - - FIX: updated icomoon icons - - FIX: code view has tabs - - FIX: code view shows mustache versions of a pattern - - FIX: patterns are properly sorted in style guide and view all views - - FIX: pattern lab-specific JS & CSS is only loaded when in the iframe - - FIX: classlist polyfill for better IE9 support - - FIX: stringified the postmessage messages for better IE9 support - - FIX: make sure history is only used by browsers that support it (e.g. IE9 doesn't) - - FIX: using watcher-launched auto-reload server now works on Windows - - FIX: various bugs with lineage - - FIX: vendored third-party JS and CSS - - FIX: infinite refresh bug squashed - - THX: thanks to @joshrcook for some styling fixes and hitting on the cause of the start-up issues on ubuntu - - THX: thanks to @tylersticka for lots of ideas: pattern states, cmd+a, boolean pattern parameters and the feedback on the watcher - - THX: thanks to @nikvm for the fix for properly sorting the styleguide view - - THX: thanks to @aarongustafson for the idea to only load the JS when it's in the iframe - -PL-v0.7.2 - - FIX: proper support for Windows with the changes that happened in v0.7.0 - - THX: thanks to @chriskevin and @MattKohnen for reporting the issue - -PL-v0.7.1 - - FIX: annotation event should only fire when overlay is active - - FIX: styleguide should properly sort patterns - - THX: thanks to @jplhomer for the heads up on the annotations issue - - THX: thanks to @tylersticka for the heads up on the styleguide issue - -PL-v0.7.0 - - ADD: auto-reload server can be started directly from the watcher - - ADD: pattern parameter support - - ADD: styleModifier support - - ADD: pseudo-pattern support - - ADD: RAM usage now outputted when generating the site - - ADD: an easter egg - - ADD: configuration flag for cleanPublic - - ADD: dedicated pattern header and footer files - - ADD: QR code generator to make mobile testing easier - - ADD: reverse lineages to see where a pattern is used - - ADD: if _data.json contains a reserved keyword an error is thrown - - ADD: closer to being PSR-0 and PSR-1 compatible - - ADD: migrator class to handle file moves/updates between versions - - ADD: configurer class to handle managing the configuration file - - FIX: ran JS hint against project JavaScript - - FIX: attempted to add better cache busting - - FIX: reorganized the project for better upgradeability by moving lots to core/ - - FIX: malformed JSON throws error and gives file name - - FIX: code view styles - - FIX: annotation styles and functionality are now more robust - - FIX: mobile styles are more robust including scrolling on iOS7 - - FIX: drop down interaction - - FIX: refactored how patterns and view all pages are gathered and generated - - FIX: lineage list now hidden if pattern doesn't have a lineage - - FIX: listitems.json and data.json default attributes match - - FIX: an existing config.ini file is automatically updated with new version - - FIX: pull bar now works in Firefox - - FIX: history now works in Firefox - - FIX: renamed the websocket servers - - THX: thanks to @faustgertz for some fixes with the new watcher class - - THX: thanks to @coding-stuff for the original idea and original code for pattern parameters - - THX: thanks to @mattwellss for inspiring the PSR compliant changes as well as changes to the configuration - -PL-v0.6.4 - - FIX: using # for a link won't cause a jump - - THX: thanks to @tylersticka for the heads up - -PL-v0.6.3 - - FIX: making sure code view is properly encoded - - THX: thanks to @tylersticka for the heads up - -PL-v0.6.2 - - FIX: a few small sass and styling tweaks - - THX: thanks to @griffinartworks for the sass and styling fixes - -PL-v0.6.1 - - FIX: fixed the height of the HTML pre element on the pattern detail view - - ADD: added in support for viewing the generated CSS on the pattern detail view - -PL-v0.6.0 - - ADD: a UI list of the current media query widths from the CSS - - ADD: a pattern's "lineage" now displays in the UI under code view - - ADD: annotations can be added to DOM elements of patterns - - ADD: separate annotation views on the list view and pattern details views - - ADD: generate() now "cleans" public/ before generating the site by deleting most everything - - ADD: added support for the css rule saver library - - ADD: can use a flag to generate the specific CSS that is used in a pattern. shows on code view when available. - - ADD: mark-up for a pattern is now included in the UI under code view - - FIX: can open the "raw" version of a pattern in a new window - - FIX: frame resizing bar properly supports decimals - - FIX: the checkboxes for the websocket-based features, page follow & auto-reload, now work - - FIX: postmessage calls now centralized and refactored - - FIX: units appear in the toolbar when using Hay! mode - - FIX: patterns shouldn't be cached - - THX: thanks to @benedfit for the MQ idea which he originally named "phases" - - THX: thanks to @alienlebarge for the "clean public/" idea - -PL-v0.3.6 - - FIX: added a delay to the watcher so the CPU doesn't get maxed - - THX: thanks to martin berglund for the heads up - -PL-v0.3.5 - - ADD: an explicit MIT license - - FIX: updated .gitignore so that it's more flexible - - THX: thanks to @alienlebarge for the .gitignore fix - -PL-v0.3.4 - - FIX: Generator class renamed because it's a reserved name in PHP 5.5 - - THX: thanks to @faustgertz for the heads up - -PL-v0.3.3 - - FIX: links created with {{ link.pattern }} now have the correct path - - FIX: links within a pattern now properly update the history - - FIX: simplified the history updates from a pattern - - THX: thanks to @kevin-heil for the fix for {{ link.pattern }} - -PL-v0.3.2 - - ADD: added .svn to the ignore dirs listing when checking the source dir - - FIX: top-level ignored dirs are now found properly - - THX: thanks to @alienlebarge for the heads up regarding .svn dirs - -PL-v0.3.1 - - FIX: made sure the command scripts work in directories that contain spaces - - THX: thanks to @mattsims for the heads up - -PL-v0.3.0 - - ADD: added "all" link to the nav that takes the user back to the style guide - - ADD: title tag updates when switching patterns - - FIX: if a pattern type or pattern sub-type doesn't have any patterns it's removed from the nav - - FIX: added styleguide.css to the pattern header - - FIX: commented out the video & audio patterns to address an issue w/ Chrome 29, frames, & History API - - FIX: reduced the number of comments called in the comment-thread pattern - - FIX: patterns won't try to auto-reload if viewed directly - - THX: thanks to @bmuenzenmeyer for the "all link" suggestion - -PL-v0.2.0 - - ADD: better styling in the overall navigation (via @geibi) - - FIX: better windows support for the generator and watcher - - FIX: more comprehensive "apache-less" support - - FIX: debian linux path issue when loading mustache - - FIX: pattern fix which mis-used an ID - - FIX: widths in 'ish are now properly updated onload - - THX: thanks to @benedfit & @bmuenzenmeyer for the help w/ windows - - THX: thanks to @geibi for the pull request with the improved nav style - - THX: thanks to @juanmi007 for alerting me to issues on debian - - THX: thanks to @lewisnyman for the pattern fix - -PL-v0.1.0 - - ADD: re-launch of the PHP version of Pattern Lab \ No newline at end of file From f251ce37a6051922011283f3e1f81bd2d27f348c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 01:12:09 -0400 Subject: [PATCH 164/166] adding a link to the changes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94577c8ae..a83e123ad 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ After installing do the following to start and view Pattern Lab: You should now be able to open [http://localhost:8080](http://localhost:8080) to see your generated site. Any changes you make in `./source/` will automatically rebuild your site and reload your browser. -As you get more comfortable with Pattern Lab you can [integrate it with a Gulp or Grunt workflow](http://patternlab.io/docs/advanced-integration-with-grunt.html) and drop some of the native Pattern Lab features like automatic browser reload. +As you get more comfortable with Pattern Lab you can [integrate it with a Gulp or Grunt workflow](http://patternlab.io/docs/advanced-integration-with-grunt.html) and drop some of the native Pattern Lab features like automatic browser reload. You can also check out [the list of plugins](http://patternlab.io/download.html). ## More Documentation @@ -60,7 +60,7 @@ Obviously Pattern Lab is deeper than the install process. [Check out the documen ## Migrating from Pattern Lab 1 to Pattern Lab 2 -Pattern Lab 2 was a complete rewrite and reorganization of Pattern Lab 1. After installing the Standard Edition for Mustache do the following to migrate from Pattern Lab 1 to Pattern Lab 2: +Pattern Lab 2 was a complete rewrite and reorganization of Pattern Lab 1. [Learn about the changes](http://patternlab.io/docs/changes-1-to-2.html). After installing the Standard Edition for Mustache do the following to migrate from Pattern Lab 1 to Pattern Lab 2: 1. Copy `./source` from your old project to your new install 2. Copy `./source/_patterns/00-atoms/00-meta/_00-head.mustache` to `./source/_meta/_00-head.mustache` From 5cdf2dd0decc1feb8b4247dd0521ec32fcf6995c Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 10:44:21 -0400 Subject: [PATCH 165/166] direction changes and handling an install via 'composer install' --- README.md | 6 +++++- composer.json | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a83e123ad..57218aab6 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,14 @@ Please follow the directions for [installing Composer](https://getcomposer.org/d Use Composer's [`create-project` command](https://getcomposer.org/doc/03-cli.md#create-project) to install the Standard Edition for Mustache into a location of your choosing. To create a project do the following: 1. In a terminal window navigate to the root of your project -2. Type `composer create-project pattern-lab/edition-mustache-webdesignday your-project-name && cd $_` +2. Type `composer create-project pattern-lab/edition-mustache-standard patternlab2-example && cd $_` This will install the Standard Edition for Mustache into a directory called `your-project-name` in `install/location/`. When prompted choose the "demo" StarterKit. You will be automatically dropped into the project directory after the process is finished. +**Note:** If you download this repository and use `composer install` to install the project dependencies you'll need to type the following to install the demo StarterKit: + + composer install-demo + ## Get Up and Running After installing do the following to start and view Pattern Lab: diff --git a/composer.json b/composer.json index fbbf96141..2e3cbceae 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "generate": "php core/console --generate", "watch": "php core/console --watch", "start": "php core/console --server --with-watch", + "install-demo": "php core/console --starterkit --install pattern-lab/starterkit-mustache-demo", "post-install-cmd": [ "PatternLab\\Installer::postInstallCmd" ], From 4e0d7ded4e8c8bd4f4e39b8ab87288b4de00e221 Mon Sep 17 00:00:00 2001 From: Dave Olsen Date: Tue, 12 Jul 2016 11:00:53 -0400 Subject: [PATCH 166/166] further clarifying the docs --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 57218aab6..4e6bbb238 100644 --- a/README.md +++ b/README.md @@ -38,15 +38,17 @@ Please follow the directions for [installing Composer](https://getcomposer.org/d Use Composer's [`create-project` command](https://getcomposer.org/doc/03-cli.md#create-project) to install the Standard Edition for Mustache into a location of your choosing. To create a project do the following: -1. In a terminal window navigate to the root of your project +1. In a terminal window navigate to where you want to install Pattern Lab 2. Type `composer create-project pattern-lab/edition-mustache-standard patternlab2-example && cd $_` -This will install the Standard Edition for Mustache into a directory called `your-project-name` in `install/location/`. When prompted choose the "demo" StarterKit. You will be automatically dropped into the project directory after the process is finished. +This will install the Standard Edition for Mustache into a directory called `patternlab2-example` in your install location. When prompted choose the "demo" StarterKit. You will be automatically dropped into the project directory after the process is finished. -**Note:** If you download this repository and use `composer install` to install the project dependencies you'll need to type the following to install the demo StarterKit: +**Note:** If you clone or download this repository and use `composer install` to install the project dependencies you'll need to type the following to install the demo StarterKit: composer install-demo +Otherwise you'll have a very bare set-up of Pattern Lab. + ## Get Up and Running After installing do the following to start and view Pattern Lab: