diff --git a/.travis.yml b/.travis.yml index 9092eb7..de4a07f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ script: cache: - directories: + - test/tmp/cache - vendor notifications: diff --git a/composer.json b/composer.json index b4ed3ec..a1818c1 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "postcss/postcss", "type": "library", - "description": "Unifficial native PHP port of the PostCSS processor", + "description": "Unofficial native PHP port of the PostCSS processor", "keywords": [ "css", "postcss", @@ -32,4 +32,4 @@ "issues": "https://github.com/mlocati/postcss/issues", "source": "https://github.com/mlocati/postcss" } -} \ No newline at end of file +} diff --git a/src/AtRule.php b/src/AtRule.php index 3535fc0..e442898 100644 --- a/src/AtRule.php +++ b/src/AtRule.php @@ -5,8 +5,7 @@ /** * Represents an at-rule. * - * If it’s followed in the CSS by a {} block, this node will have - * a nodes property representing its children. + * If it's followed in the CSS by a {} block, this node will have a nodes property representing its children. * * @example * $root = \PostCSS\Parser::parse('@charset "UTF-8"; @media print {}'); @@ -21,7 +20,7 @@ class AtRule extends Container { /** - * The at-rule’s name immediately follows the `@`. + * The at-rule's name immediately follows the `@`. * * @example * $root = \PostCSS\Parser::parse('@media print {}'); @@ -33,7 +32,7 @@ class AtRule extends Container public $name = null; /** - * The at-rule’s parameters, the values that follow the at-rule’s name but precede any {} block. + * The at-rule's parameters, the values that follow the at-rule's name but precede any {} block. * * @example * $root = \PostCSS\Parser::parse('@media print, screen {}'); diff --git a/src/Comment.php b/src/Comment.php index 6634d6c..b9f9f61 100644 --- a/src/Comment.php +++ b/src/Comment.php @@ -12,7 +12,7 @@ class Comment extends Node { /** - * The comment’s text. + * The comment's text. * * @var string|null */ diff --git a/src/Container.php b/src/Container.php index 0ff873b..f09222e 100644 --- a/src/Container.php +++ b/src/Container.php @@ -9,8 +9,8 @@ * * @link https://github.com/postcss/postcss/blob/master/lib/container.es6 * - * @property Node|null $first The container’s first child (@example $rule->first == $rules->nodes[0]) ) - * @property Node|null $last The container’s last child (@example $rule->last == $rules->nodes[count($rules->nodes) - 1] ) + * @property Node|null $first The container's first child (@example $rule->first == $rules->nodes[0]) ) + * @property Node|null $last The container's last child (@example $rule->last == $rules->nodes[count($rules->nodes) - 1] ) */ abstract class Container extends Node { diff --git a/src/Exception/CssSyntaxError.php b/src/Exception/CssSyntaxError.php index 960d5f4..fb9df78 100644 --- a/src/Exception/CssSyntaxError.php +++ b/src/Exception/CssSyntaxError.php @@ -8,15 +8,14 @@ /** * The CSS parser throws this error for broken CSS. * - * Custom parsers can throw this error for broken custom syntax using - * the {@link Node#error} method. + * Custom parsers can throw this error for broken custom syntax using Node->error method. * * PostCSS will use the input source map to detect the original error location. - * If you wrote a Sass file, compiled it to CSS and then parsed it with PostCSS, - * PostCSS will show the original position in the Sass file. + * If you wrote a Sass file, compiled it to CSS and then parsed it with PostCSS, PostCSS will show the original position in the Sass file. * - * If you need the position in the PostCSS input - * (e.g., to debug the previous compiler), use `error.input.file`. + * If you need the position in the PostCSS input (e.g., to debug the previous compiler), use `$error->input['file']`. + * + * @link https://github.com/postcss/postcss/blob/master/lib/css-syntax-error.es6 */ class CssSyntaxError extends Exception { @@ -90,6 +89,9 @@ public function setMessage($message) $this->message = (string) $message; } + /** + * Updates the message. + */ public function updateMessage() { $plugin = ($this->postcssPlugin instanceof PluginInterface) ? $this->postcssPlugin->getName() : (string) $this->postcssPlugin; @@ -208,6 +210,11 @@ public function showSourceCode($color = null) return implode("\n", $output); } + /** + * {@inheritdoc} + * + * @see Exception::__toString() + */ public function __toString() { $fqn = get_class($this); diff --git a/src/LazyResult.php b/src/LazyResult.php index 2985c2c..7376895 100644 --- a/src/LazyResult.php +++ b/src/LazyResult.php @@ -2,8 +2,9 @@ namespace PostCSS; -use React\Promise\Promise; use PostCSS\Plugin\PluginInterface; +use React\Promise\Promise; +use React\Promise\PromiseInterface; /** * A Promise proxy for the result of PostCSS transformations. @@ -50,6 +51,18 @@ class LazyResult */ protected $processing = null; + /** + * Current plugin index. + * + * @var int + */ + protected $plugin; + + /** + * @param Processor $processor + * @param mixed $css + * @param array $opts + */ public function __construct(Processor $processor, $css, array $opts = []) { $this->stringified = false; @@ -122,10 +135,9 @@ public function __get($name) } /** - * Processes input CSS through synchronous plugins - * and calls {@link Result#warnings()}. + * Processes input CSS through synchronous plugins and calls Result->warnings(). * - * @return {Warning[]} warnings from plugins + * @return Warning[] warnings from plugins */ public function warnings() { @@ -133,12 +145,12 @@ public function warnings() } /** - * Alias for the {@link LazyResult#css} property. + * Alias for the LazyResult->css property. * * @example - * lazy + '' === lazy.css; + * (string) $lazy === $lazy->css; * - * @return {string} output CSS + * @return string output CSS */ public function __toString() { @@ -146,22 +158,18 @@ public function __toString() } /** - * Processes input CSS through synchronous and asynchronous plugins - * and calls `onFulfilled` with a Result instance. If a plugin throws - * an error, the `onRejected` callback will be executed. + * Processes input CSS through synchronous and asynchronous plugins and calls `onFulfilled` with a Result instance. + * If a plugin throws an error, the `onRejected` callback will be executed. * * It implements standard Promise API. * - * @param {onFulfilled} onFulfilled - callback will be executed - * when all plugins will finish work - * @param {onRejected} onRejected - callback will be execited on any error + * @param callable $onFulfilled Callback will be executed when all plugins will finish work + * @param callable $onRejected Callback will be executed on any error * - * @return {Promise} Promise API to make queue + * @return Promise Promise API to make queue * * @example - * postcss([cssnext]).process(css).then(result => { - * console.log(result.css); - * }); + * (new \PostCSS\Processor([Cssnext::class]))->process($css)->then(function ($result) { echo $result->css; })->done(); */ public function then($onFulfilled, $onRejected) { @@ -169,20 +177,18 @@ public function then($onFulfilled, $onRejected) } /** - * Processes input CSS through synchronous and asynchronous plugins - * and calls onRejected for each error thrown in any plugin. - * + * Processes input CSS through synchronous and asynchronous plugins and calls onRejected for each error thrown in any plugin. * It implements standard Promise API. * - * @param {onRejected} onRejected - callback will be execited on any error + * @param callable $onRejected Callback will be executed on any error * - * @return {Promise} Promise API to make queue + * @return Promise Promise API to make queue * * @example - * postcss([cssnext]).process(css).then(result => { - * console.log(result.css); - * }).catch(error => { - * console.error(error); + * (new \PostCSS\Processor([Cssnext::class]))->process($css)->then(function ($result) { + * echo $result->css); + * })->catchError(function ($error) { + * echo (string) $error; * }); */ public function catchError($onRejected) @@ -193,6 +199,10 @@ public function catchError($onRejected) ); } + /** + * @param \Exception $error + * @param PluginInterface|string $plugin + */ public function handleError(\Exception $error, $plugin) { try { @@ -216,6 +226,12 @@ public function handleError(\Exception $error, $plugin) } } + /** + * @param callable $resolve + * @param callable $reject + * + * @return mixed + */ public function asyncTick($resolve, $reject) { if ($this->plugin >= count($this->result->processor->plugins)) { @@ -228,7 +244,7 @@ public function asyncTick($resolve, $reject) $promise = $this->run($plugin); $this->plugin += 1; - if ($promise instanceof Promise) { + if ($promise instanceof PromiseInterface) { $me = $this; $promise->then( function () use ($me, $resolve, $reject) { @@ -249,6 +265,9 @@ function ($error) use ($me, $plugin, $reject) { } } + /** + * @return Promise + */ public function async() { if ($this->processed) { @@ -284,6 +303,11 @@ function () use ($me) { return $this->processing; } + /** + * @throws \Exception + * + * @return \PostCSS\Result + */ public function sync() { if ($this->processed) { @@ -292,7 +316,7 @@ public function sync() $this->processed = true; if ($this->processing) { - throw new \Exception('Use process(css).then(cb) to work with async plugins'); + throw new \Exception('Use process($css)->then($cb) to work with async plugins'); } if ($this->error) { @@ -303,13 +327,20 @@ public function sync() for ($i = 0; $i < count($processor->plugins); ++$i) { $promise = $this->run($processor->plugins[$i]); if ($promise instanceof Promise) { - throw new \Exception('Use process(css).then(cb) to work with async plugins'); + throw new \Exception('Use process($css)->then($cb) to work with async plugins'); } } return $this->result; } + /** + * @param PluginInterface $plugin + * + * @throws \Exception + * + * @return mixed + */ public function run(PluginInterface $plugin) { $this->result->lastPlugin = $plugin; diff --git a/src/ListUtil.php b/src/ListUtil.php index 72dc964..fde540e 100644 --- a/src/ListUtil.php +++ b/src/ListUtil.php @@ -67,7 +67,7 @@ public static function split($string, array $separators, $last = false) * * @param string $string Space-separated values * - * @return string[] Splitted values + * @return string[] Split values * * @example * @@ -85,7 +85,7 @@ public static function space($string) * * @param string $string Comma-separated values * - * @return string[] Splitted values + * @return string[] Split values * * @example * \PostCSS\ListUtil::comma('black, linear-gradient(white, black)') //=> ['black', 'linear-gradient(white, black)'] diff --git a/src/MapGenerator.php b/src/MapGenerator.php index 5528b74..82245f6 100644 --- a/src/MapGenerator.php +++ b/src/MapGenerator.php @@ -2,8 +2,8 @@ namespace PostCSS; -use PostCSS\SourceMap\Consumer\Consumer; use PostCSS\Path\NodeJS as Path; +use PostCSS\SourceMap\Consumer\Consumer; /** * @link https://github.com/postcss/postcss/blob/master/lib/map-generator.es6 @@ -45,6 +45,11 @@ class MapGenerator */ protected $map = null; + /** + * @param callable $stringify + * @param Root $root + * @param array $opts + */ public function __construct(callable $stringify, Root $root, array $opts = []) { $this->stringify = $stringify; @@ -53,6 +58,9 @@ public function __construct(callable $stringify, Root $root, array $opts = []) $this->opts = $opts; } + /** + * @return bool + */ public function isMap() { if (isset($this->opts['map'])) { @@ -64,6 +72,9 @@ public function isMap() } } + /** + * @return PreviousMap[] + */ public function previous() { if ($this->previousMaps === null) { @@ -82,6 +93,9 @@ public function previous() return $this->previousMaps; } + /** + * @return bool + */ public function isInline() { if (isset($this->mapOpts['inline'])) { @@ -109,6 +123,9 @@ public function isInline() } } + /** + * @return bool + */ public function isSourcesContent() { if (isset($this->mapOpts['sourcesContent'])) { @@ -151,7 +168,7 @@ public function setSourcesContent() $me = $this; $already = []; $this->root->walk(function (Node $node) use ($me, &$already) { - if ($node->source) { + if ($node->source && isset($node->source['input'])) { $from = $node->source['input']->from; if ($from && !isset($already[$from])) { $already[$from] = true; @@ -180,6 +197,9 @@ public function applyPrevMaps() } } + /** + * @return bool + */ public function isAnnotation() { if ($this->isInline()) { @@ -222,6 +242,9 @@ public function addAnnotation() $this->css .= $eol.'/*# sourceMappingURL='.$content.' */'; } + /** + * @return string + */ public function outputFile() { if (isset($this->opts['to']) && $this->opts['to']) { @@ -233,6 +256,9 @@ public function outputFile() } } + /** + * @return array First array item is the css. If not inline, there's a second item (a SourceMap\Generator instance) + */ public function generateMap() { $this->generateString(); @@ -254,9 +280,14 @@ public function generateMap() } } + /** + * @param string $file + * + * @return string + */ public function relative($file) { - if (preg_match('/^\w+:\/\//', $file)) { + if ($file && ($file[0] === '<' || preg_match('/^\w+:\/\//', $file))) { return $file; } @@ -274,7 +305,12 @@ public function relative($file) } } - public function sourcePath($node) + /** + * @param Node $node + * + * @return string + */ + public function sourcePath(Node $node) { if (isset($this->mapOpts['from']) && $this->mapOpts['from']) { return $this->mapOpts['from']; @@ -364,6 +400,9 @@ function ($str, Node $node = null, $type = null) use ($me, &$line, &$column) { ); } + /** + * @return array First array item is the css. If not inline, there's a second item (a SourceMap\Generator instance) + */ public function generate() { $this->clearAnnotation(); diff --git a/src/Node.php b/src/Node.php index 9d6e043..c2cb6da 100644 --- a/src/Node.php +++ b/src/Node.php @@ -7,7 +7,7 @@ * * @link https://github.com/postcss/postcss/blob/master/lib/node.es6 */ -class Node +abstract class Node { /** * Possible values are `root`, `atrule`, `rule`, `decl`, or `comment`. @@ -17,7 +17,7 @@ class Node public $type = null; /** - * The node’s parent node. + * The node's parent node. * * @var Container|null */ @@ -43,15 +43,29 @@ class Node * - `afterName`: the space between the at-rule name and its parameters. * - Comment: * - `before`: the space symbols before the node. - * - `left`: the space symbols between `/*` and the comment’s text. - * - `right`: the space symbols between the comment’s text. + * - `left`: the space symbols between `/*` and the comment's text. + * - `right`: the space symbols between the comment's text. + * - Root: + * - `after`: the space symbols after the last child to the end of file. + * - `semicolon`: is the last child has an (optional) semicolon. + * - Rule: + * - `before`: the space symbols before the node. It also stores `*` and `_` symbols before the declaration (IE hack). + * - `after`: the space symbols after the last child of the node to the end of the node. + * - `between`: the symbols between the property and value for declarations, selector and `{` for rules, or last parameter and `{` for at-rules. + * - `semicolon`: contains true if the last child has an (optional) semicolon. * * PostCSS cleans at-rule parameters from comments and extra spaces, but it stores origin content in raws properties. - * As such, if you don’t change a declaration’s value, PostCSS will use the raw value with comments. + * As such, if you don't change a declaration's value, PostCSS will use the raw value with comments. * * @example - * $root = \PostCSS\Parser::parse(' @media\nprint {\n}') + * $root = \PostCSS\Parser::parse(' @media\nprint {\n}'); * $root->first->first->raws //=> ['before' => ' ', 'between' => ' ', 'afterName' = > "\n", 'after' => "\n"] + * @example + * \PostCSS\Parser::parse('a {}\n').raws //=> ['after' => "\n"] + * \PostCSS\Parser::parse('a {}').raws //=> ['after => ''] + * @example + * $root = \PostCSS\Parser::parse('a {\n color:black\n}'); + * $root->first->first->raws //=> ['before' => '', 'between' => ' ', 'after' => "\n"] * * @var Raws */ diff --git a/src/Parser.php b/src/Parser.php index 896ba2c..45d4918 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -12,26 +12,6 @@ */ class Parser { - const SINGLE_QUOTE = 0x27; //"'" - const DOUBLE_QUOTE = 0x22; //'"' - const BACKSLASH = 0x5c; //"\\" - const SLASH = 0x2f; //"/" - const NEWLINE = 0x0a; //"\n" - const SPACE = 0x20; //" " - const FEED = 0x0c; //"\f" - const TAB = 0x09; //"\t" - const CR = 0x0d; //"\r" - const OPEN_SQUARE = 0x5b; //"[" - const CLOSE_SQUARE = 0x5d; //"]" - const OPEN_PARENTHESES = 0x28; //"(" - const CLOSE_PARENTHESES = 0x29; //")" - const OPEN_CURLY = 0x7b; //"{" - const CLOSE_CURLY = 0x7d; //"}" - const SEMICOLON = 0x3b; //";" - const ASTERICK = 0x2a; //"*" - const COLON = 0x3a; //":" - const AT = 0x40; //"@" - const RE_AT_END = '/[ \\n\\t\\r\\f\\{\\(\\)\'"\\\\;\\/\\[\\]#]/'; const RE_WORD_END = '/[ \\n\\t\\r\\f\\(\\)\\{\\}:;@!\'"\\\\\\]\\[#]|\\/(?=\\*)/'; const RE_BAD_BRACKET = '%.[\\\/\("\'\n]%'; @@ -535,60 +515,60 @@ public static function getTokens(Input $input, array $options = []) }; while ($pos < $length) { - $code = ord($css[$pos]); + $code = $css[$pos]; - if ($code === static::NEWLINE || $code === static::FEED || $code === static::CR && ($pos === $length - 1 || ord($css[$pos + 1]) !== static::NEWLINE)) { + if ($code === "\n" || $code === "\f" || $code === "\r" && ($pos === $length - 1 || $css[$pos + 1] !== "\n")) { $offset = $pos; $line += 1; } switch ($code) { - case static::NEWLINE: - case static::SPACE: - case static::TAB: - case static::CR: - case static::FEED: + case "\n": + case ' ': + case "\t": + case "\r": + case "\f": $next = $pos; do { $next += 1; - $code = ($next === $length) ? null : ord($css[$next]); - if ($code === static::NEWLINE) { + $code = ($next === $length) ? null : $css[$next]; + if ($code === "\n") { $offset = $next; $line += 1; } - } while ($code === static::SPACE || $code === static::NEWLINE || $code === static::TAB || $code === static::CR || $code === static::FEED); + } while ($code === ' ' || $code === "\n" || $code === "\t" || $code === "\r" || $code === "\f"); $tokens[] = ['space', substr($css, $pos, $next - $pos)]; $pos = $next - 1; break; - case static::OPEN_SQUARE: + case '[': $tokens[] = ['[', '[', $line, $pos - $offset]; break; - case static::CLOSE_SQUARE: + case ']': $tokens[] = [']', ']', $line, $pos - $offset]; break; - case static::OPEN_CURLY: + case '{': $tokens[] = ['{', '{', $line, $pos - $offset]; break; - case static::CLOSE_CURLY: + case '}': $tokens[] = ['}', '}', $line, $pos - $offset]; break; - case static::COLON: + case ':': $tokens[] = [':', ':', $line, $pos - $offset]; break; - case static::SEMICOLON: + case ';': $tokens[] = [';', ';', $line, $pos - $offset]; break; - case static::OPEN_PARENTHESES: + case '(': $prev = empty($tokens) ? '' : $tokens[count($tokens) - 1][1]; - $n = ($pos < $length - 1) ? ord($css[$pos + 1]) : null; - if ($prev === 'url' && $n !== static::SINGLE_QUOTE && $n !== static::DOUBLE_QUOTE && $n !== static::SPACE && $n !== static::NEWLINE && $n !== static::TAB && $n !== static::FEED && $n !== static::CR) { + $n = ($pos < $length - 1) ? $css[$pos + 1] : null; + if ($prev === 'url' && $n !== "'" && $n !== '"' && $n !== ' ' && $n !== "\n" && $n !== "\t" && $n !== "\f" && $n !== "\r") { $next = $pos; do { $escaped = false; @@ -602,7 +582,7 @@ public static function getTokens(Input $input, array $options = []) } } $escapePos = $next; - while (ord($css[$escapePos - 1]) === static::BACKSLASH) { + while ($css[$escapePos - 1] === '\\') { $escapePos -= 1; $escaped = !$escaped; } @@ -623,13 +603,13 @@ public static function getTokens(Input $input, array $options = []) } break; - case static::CLOSE_PARENTHESES: + case ')': $tokens[] = [')', ')', $line, $pos - $offset]; break; - case static::SINGLE_QUOTE: - case static::DOUBLE_QUOTE: - $quote = $code === static::SINGLE_QUOTE ? '\'' : '"'; + case "'": + case '"': + $quote = $code === "'" ? '\'' : '"'; $next = $pos; do { $escaped = false; @@ -643,7 +623,7 @@ public static function getTokens(Input $input, array $options = []) } } $escapePos = $next; - while (ord($css[$escapePos - 1]) === static::BACKSLASH) { + while ($css[$escapePos - 1] === '\\') { $escapePos -= 1; $escaped = !$escaped; } @@ -668,7 +648,7 @@ public static function getTokens(Input $input, array $options = []) $pos = $next; break; - case static::AT: + case '@': if (!preg_match(static::RE_AT_END, $css, $rxMatches, 0, $pos + 1)) { $next = $length - 1; } else { @@ -678,15 +658,15 @@ public static function getTokens(Input $input, array $options = []) $pos = $next; break; - case static::BACKSLASH: + case '\\': $next = $pos; $escape = true; - while (($next < ($length - 1)) && ord($css[$next + 1]) === static::BACKSLASH) { + while (($next < ($length - 1)) && $css[$next + 1] === '\\') { $next += 1; $escape = !$escape; } - $code = ($next < ($length - 1)) ? ord($css[$next + 1]) : null; - if ($escape && ($code !== static::SLASH && $code !== static::SPACE && $code !== static::NEWLINE && $code !== static::TAB && $code !== static::CR && $code !== static::FEED)) { + $code = ($next < ($length - 1)) ? $css[$next + 1] : null; + if ($escape && ($code !== '/' && $code !== ' ' && $code !== "\n" && $code !== "\t" && $code !== "\r" && $code !== "\f")) { $next += 1; } $tokens[] = ['word', substr($css, $pos, $next + 1 - $pos), $line, $pos - $offset, $line, $next - $offset]; @@ -694,7 +674,7 @@ public static function getTokens(Input $input, array $options = []) break; default: - if ($code === static::SLASH && $pos < ($length - 1) && ord($css[$pos + 1]) === static::ASTERICK) { + if ($code === '/' && $pos < ($length - 1) && $css[$pos + 1] === '*') { $next = strpos($css, '*/', $pos + 2); if ($next === false) { if ($ignore) { diff --git a/src/Path/NodeJS.php b/src/Path/NodeJS.php index a84c366..03d32a8 100644 --- a/src/Path/NodeJS.php +++ b/src/Path/NodeJS.php @@ -59,7 +59,7 @@ protected static function posix_resolve(array $paths) } $resolvedPath = $path.'/'.$resolvedPath; - $resolvedAbsolute = ord($path[0]) === 47/*/*/; + $resolvedAbsolute = $path[0] === '/'; } // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) @@ -123,28 +123,28 @@ protected static function win32_resolve(array $paths) } $rootEnd = 0; - $code = ord($path[0]); + $code = $path[0]; $device = ''; $isAbsolute = false; // Try to match a root if ($len > 1) { - if ($code === 47/*/*/ || $code === 92/*\*/) { + if ($code === '/' || $code === '\\') { // Possible UNC root // If we started with a separator, we know we at least have an // absolute path of some kind (UNC or otherwise) $isAbsolute = true; - $code = ord($path[1]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[1]; + if ($code === '/' || $code === '\\') { // Matched double path separator at beginning $j = 2; $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -154,8 +154,8 @@ protected static function win32_resolve(array $paths) $last = $j; // Match 1 or more path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code !== 47/*/*/ && $code !== 92/*\*/) { + $code = $path[$j]; + if ($code !== '/' && $code !== '\\') { break; } } @@ -164,8 +164,8 @@ protected static function win32_resolve(array $paths) $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -183,15 +183,15 @@ protected static function win32_resolve(array $paths) } else { $rootEnd = 1; } - } elseif (($code >= 65/*A*/ && $code <= 90/*Z*/) || ($code >= 97/*a*/ && $code <= 122/*z*/)) { + } elseif (($code >= 'A' && $code <= 'Z') || ($code >= 'a' && $code <= 'z')) { // Possible device root - $code = ord($path[1]); - if ($code === 58/*:*/) { + $code = $path[1]; + if ($code === ':') { $device = substr($path, 0, 2); $rootEnd = 2; if ($len > 2) { - $code = ord($path[2]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[2]; + if ($code === '/' || $code === '\\') { // Treat separator following drive name as an absolute path // indicator $isAbsolute = true; @@ -200,7 +200,7 @@ protected static function win32_resolve(array $paths) } } } - } elseif ($code === 47/*/*/ || $code === 92/*\*/) { + } elseif ($code === '/' || $code === '\\') { // `path` contains just a path separator $rootEnd = 1; $isAbsolute = true; @@ -254,23 +254,23 @@ protected static function normalizeStringWin32($path, $allowAboveRoot) $pathLength = strlen($path); for ($i = 0; $i <= $pathLength; ++$i) { if ($i < $pathLength) { - $code = ord($path[$i]); - } elseif ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$i]; + } elseif ($code === '/' || $code === '\\') { break; } else { - $code = 47/*/*/; + $code = '/'; } - if ($code === 47/*/*/ || $code === 92/*\*/) { + if ($code === '/' || $code === '\\') { if ($lastSlash === $i - 1 || $dots === 1) { // NOOP } elseif ($lastSlash !== $i - 1 && $dots === 2) { $resLength = strlen($res); - if ($resLength < 2 || ord($res[$resLength - 1]) !== 46/*.*/ || ord($res[$resLength - 2]) !== 46/*.*/) { + if ($resLength < 2 || $res[$resLength - 1] !== '.' || $res[$resLength - 2] !== '.') { if ($resLength > 2) { $start = $resLength - 1; $j = $start; for (; $j >= 0; --$j) { - if (ord($res[$j]) === 92/*\*/) { + if ($res[$j] === '\\') { break; } } @@ -307,7 +307,7 @@ protected static function normalizeStringWin32($path, $allowAboveRoot) } $lastSlash = $i; $dots = 0; - } elseif ($code === 46/*.*/ && $dots !== -1) { + } elseif ($code === '.' && $dots !== -1) { ++$dots; } else { $dots = -1; @@ -335,23 +335,23 @@ protected static function normalizeStringPosix($path, $allowAboveRoot) $pathLength = strlen($path); for ($i = 0; $i <= $pathLength; ++$i) { if ($i < $pathLength) { - $code = ord($path[$i]); - } elseif ($code === 47/*/*/) { + $code = $path[$i]; + } elseif ($code === '/') { break; } else { - $code = 47/*/*/; + $code = '/'; } - if ($code === 47/*/*/) { + if ($code === '/') { if ($lastSlash === $i - 1 || $dots === 1) { // NOOP } elseif ($lastSlash !== $i - 1 && $dots === 2) { $resLength = strlen($res); - if ($resLength < 2 || ord($res[$resLength - 1]) !== 46/*.*/ || ord($res[$resLength - 2]) !== 46/*.*/) { + if ($resLength < 2 || $res[$resLength - 1] !== '.' || $res[$resLength - 2] !== '.') { if ($resLength > 2) { $start = $resLength - 1; $j = $start; for (; $j >= 0; --$j) { - if (ord($res[$j]) === 47/*/*/) { + if ($res[$j] === '/') { break; } } @@ -388,7 +388,7 @@ protected static function normalizeStringPosix($path, $allowAboveRoot) } $lastSlash = $i; $dots = 0; - } elseif ($code === 46/*.*/ && $dots !== -1) { + } elseif ($code === '.' && $dots !== -1) { ++$dots; } else { $dots = -1; @@ -455,14 +455,14 @@ protected static function win32_relative($from, $to) $fromStart = 0; $fl = strlen($from); for (; $fromStart < $fl; ++$fromStart) { - if (ord($from[$fromStart]) !== 92/*\*/) { + if ($from[$fromStart] !== '\\') { break; } } // Trim trailing backslashes (applicable $to UNC paths only) $fromEnd = $fl; for (; $fromEnd - 1 > $fromStart; --$fromEnd) { - if (ord($from[$fromEnd - 1]) !== 92/*\*/) { + if ($from[$fromEnd - 1] !== '\\') { break; } } @@ -471,14 +471,14 @@ protected static function win32_relative($from, $to) $tl = strlen($to); $toStart = 0; for (; $toStart < $tl; ++$toStart) { - if (ord($to[$toStart]) !== 92/*\*/) { + if ($to[$toStart] !== '\\') { break; } } // Trim trailing backslashes (applicable $to UNC paths only) $toEnd = $tl; for (; $toEnd - 1 > $toStart; --$toEnd) { - if (ord($to[$toEnd - 1]) !== 92/*\*/) { + if ($to[$toEnd - 1] !== '\\') { break; } } @@ -490,7 +490,7 @@ protected static function win32_relative($from, $to) for (; $i <= $length; ++$i) { if ($i === $length) { if ($toLen > $length) { - if (ord($to[$toStart + $i]) === 92/*\*/) { + if ($to[$toStart + $i] === '\\') { // We get here if `$from` is the exact base path for `$to`. // For example: $from='C:\\foo\\bar'; $to='C:\\foo\\bar\\baz' return substr($toOrig, $toStart + $i + 1); @@ -501,7 +501,7 @@ protected static function win32_relative($from, $to) } } if ($fromLen > $length) { - if (ord($from[$fromStart + $i]) === 92/*\*/) { + if ($from[$fromStart + $i] === '\\') { // We get here if `$to` is the exact base path for `$from`. // For example: $from='C:\\foo\\bar'; $to='C:\\foo' $lastCommonSep = $i; @@ -513,11 +513,11 @@ protected static function win32_relative($from, $to) } break; } - $fromCode = ord($from[$fromStart + $i]); - $toCode = ord($to[$toStart + $i]); + $fromCode = $from[$fromStart + $i]; + $toCode = $to[$toStart + $i]; if ($fromCode !== $toCode) { break; - } elseif ($fromCode === 92/*\*/) { + } elseif ($fromCode === '\\') { $lastCommonSep = $i; } } @@ -537,7 +537,7 @@ protected static function win32_relative($from, $to) } // Generate the relative path based on the path difference between `$to` and `$from` for ($i = $fromStart + $lastCommonSep + 1; $i <= $fromEnd; ++$i) { - if ($i === $fromEnd || ord($from[$i]) === 92/*\*/) { + if ($i === $fromEnd || $from[$i] === '\\') { if ($out === '') { $out = '..'; } else { @@ -551,7 +551,7 @@ protected static function win32_relative($from, $to) return $out.substr($toOrig, $toStart + $lastCommonSep, $toEnd - $toStart + $lastCommonSep); } else { $toStart .= $lastCommonSep; - if (ord($toOrig[$toStart]) === 92/*\*/) { + if ($toOrig[$toStart] === '\\') { ++$toStart; } @@ -589,7 +589,7 @@ protected static function posix_relative($from, $to) $fromStart = 1; $fl = strlen($from); for (; $fromStart < $fl; ++$fromStart) { - if (ord($from[$fromStart]) !== 47/*/*/) { + if ($from[$fromStart] !== '/') { break; } } @@ -599,7 +599,7 @@ protected static function posix_relative($from, $to) $toStart = 1; $tl = strlen($to); for (; $toStart < $tl; ++$toStart) { - if (ord($to[$toStart]) !== 47/*/*/) { + if ($to[$toStart] !== '/') { break; } } @@ -612,7 +612,7 @@ protected static function posix_relative($from, $to) for (; $i <= $length; ++$i) { if ($i === $length) { if ($toLen > $length) { - if (ord($to[$toStart + $i]) === 47/*/*/) { + if ($to[$toStart + $i] === '/') { // We get here if `$from` is the exact base path for `$to`. // For example: $from='/foo/bar'; $to='/foo/bar/baz' return substr($to, $toStart + $i + 1); @@ -622,7 +622,7 @@ protected static function posix_relative($from, $to) return substr($to, $toStart + $i); } } elseif ($fromLen > $length) { - if (ord($from[$fromStart + $i]) === 47/*/*/) { + if ($from[$fromStart + $i] === '/') { // We get here if `$to` is the exact base path for `$from`. // For example: $from='/foo/bar/baz'; $to='/foo/bar' $lastCommonSep = $i; @@ -634,18 +634,18 @@ protected static function posix_relative($from, $to) } break; } - $fromCode = ord($from[$fromStart + $i]); - $toCode = ord($to[$toStart + $i]); + $fromCode = $from[$fromStart + $i]; + $toCode = $to[$toStart + $i]; if ($fromCode !== $toCode) { break; - } elseif ($fromCode === 47/*/*/) { + } elseif ($fromCode === '/') { $lastCommonSep = $i; } } $out = ''; // Generate the relative path based on the path difference between `$to` and `$from` for ($i = $fromStart + $lastCommonSep + 1; $i <= $fromEnd; ++$i) { - if ($i === $fromEnd || ord($from[$i]) === 47/*/*/) { + if ($i === $fromEnd || $from[$i] === '/') { if ($out === '') { $out = '..'; } else { @@ -658,7 +658,7 @@ protected static function posix_relative($from, $to) return $out.substr($to, $toStart + $lastCommonSep); } else { $toStart += $lastCommonSep; - if (ord($to[$toStart]) === 47/*/*/) { + if ($to[$toStart] === '/') { ++$toStart; } @@ -706,22 +706,22 @@ protected static function win32_dirname($path) $end = -1; $matchedSlash = true; $offset = 0; - $code = ord($path[0]); + $code = $path[0]; // Try to match a root if ($len > 1) { - if ($code === 47/*/*/ || $code === 92/*\*/) { + if ($code === '/' || $code === '\\') { // Possible UNC root $rootEnd = $offset = 1; - $code = ord($path[1]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[1]; + if ($code === '/' || $code === '\\') { // Matched double path separator at beginning $j = 2; $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -730,8 +730,8 @@ protected static function win32_dirname($path) $last = $j; // Match 1 or more path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code !== 47/*/*/ && $code !== 92/*\*/) { + $code = $path[$j]; + if ($code !== '/' && $code !== '\\') { break; } } @@ -740,8 +740,8 @@ protected static function win32_dirname($path) $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -758,26 +758,26 @@ protected static function win32_dirname($path) } } } - } elseif (($code >= 65/*A*/ && $code <= 90/*Z*/) || ($code >= 97/*a*/ && $code <= 122/*z*/)) { + } elseif (($code >= 'A' && $code <= 'Z') || ($code >= 'a' && $code <= 'z')) { // Possible device root - $code = ord($path[1]); - if (ord($path[1]) === 58/*:*/) { + $code = $path[1]; + if ($path[1] === ':') { $rootEnd = $offset = 2; if ($len > 2) { - $code = ord($path[2]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[2]; + if ($code === '/' || $code === '\\') { $rootEnd = $offset = 3; } } } } - } elseif ($code === 47/*/*/ || $code === 92/*\*/) { + } elseif ($code === '/' || $code === '\\') { return $path[0]; } for ($i = $len - 1; $i >= $offset; --$i) { - $code = ord($path[$i]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$i]; + if ($code === '/' || $code === '\\') { if (!$matchedSlash) { $end = $i; break; @@ -817,13 +817,13 @@ protected static function posix_dirname($path) if ($len === 0) { return '.'; } - $code = ord($path[0]); - $hasRoot = ($code === 47/*/*/); + $code = $path[0]; + $hasRoot = ($code === '/'); $end = -1; $matchedSlash = true; for ($i = $len - 1; $i >= 1; --$i) { - $code = ord($path[$i]); - if ($code === 47/*/*/) { + $code = $path[$i]; + if ($code === '/') { if (!$matchedSlash) { $end = $i; break; @@ -894,17 +894,17 @@ protected static function win32_join(array $arguments) // var firstPart = paths[0]; $needsReplace = true; $slashCount = 0; - $code = ord($firstPart[0]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $firstPart[0]; + if ($code === '/' || $code === '\\') { ++$slashCount; $firstLen = strlen($firstPart); if ($firstLen > 1) { - $code = ord($firstPart[1]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $firstPart[1]; + if ($code === '/' || $code === '\\') { ++$slashCount; if ($firstLen > 2) { - $code = ord($firstPart[2]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $firstPart[2]; + if ($code === '/' || $code === '\\') { ++$slashCount; } else { // We matched a UNC path in the first part @@ -917,8 +917,8 @@ protected static function win32_join(array $arguments) if ($needsReplace) { // Find any more consecutive slashes we need to replace for (; $slashCount < strlen($joined); ++$slashCount) { - $code = ord($joined[$slashCount]); - if ($code !== 47/*/*/ && $code !== 92/*\*/) { + $code = $joined[$slashCount]; + if ($code !== '/' && $code !== '\\') { break; } } @@ -999,26 +999,26 @@ protected static function win32_normalize($path) return '.'; } $rootEnd = 0; - $code = ord($path[0]); + $code = $path[0]; $device = null; $isAbsolute = false; // Try to match a root if ($len > 1) { - if ($code === 47/*/*/ || $code === 92/*\*/) { + if ($code === '/' || $code === '\\') { // Possible UNC root // If we started with a separator, we know we at least have an absolute path of some kind (UNC or otherwise) $isAbsolute = true; - $code = ord($path[1]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[1]; + if ($code === '/' || $code === '\\') { // Matched double path separator at beginning $j = 2; $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -1028,8 +1028,8 @@ protected static function win32_normalize($path) $last = $j; // Match 1 or more path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code !== 47/*/*/ && $code !== 92/*\*/) { + $code = $path[$j]; + if ($code !== '/' && $code !== '\\') { break; } } @@ -1038,8 +1038,8 @@ protected static function win32_normalize($path) $last = $j; // Match 1 or more non-path separators for (; $j < $len; ++$j) { - $code = ord($path[$j]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[$j]; + if ($code === '/' || $code === '\\') { break; } } @@ -1059,16 +1059,16 @@ protected static function win32_normalize($path) } else { $rootEnd = 1; } - } elseif (($code >= 65/*A*/ && $code <= 90/*Z*/) || ($code >= 97/*a*/ && $code <= 122/*z*/)) { + } elseif (($code >= 'A' && $code <= 'Z') || ($code >= 'a' && $code <= 'z')) { // Possible device root - $code = ord($path[1]); - if (ord($path[1]) === 58/*:*/) { + $code = $path[1]; + if ($path[1] === ':') { $device = substr($path, 0, 2); $rootEnd = 2; if ($len > 2) { - $code = ord($path[2]); - if ($code === 47/*/*/ || $code === 92/*\*/) { + $code = $path[2]; + if ($code === '/' || $code === '\\') { // Treat separator following drive name as an absolute path indicator $isAbsolute = true; $rootEnd = 3; @@ -1076,13 +1076,13 @@ protected static function win32_normalize($path) } } } - } elseif ($code === 47/*/*/ || $code === 92/*\*/) { + } elseif ($code === '/' || $code === '\\') { // `path` contains just a path separator, exit early to avoid unnecessary work return '\\'; } - $code = ord($path[$len - 1]); - $trailingSeparator = ($code === 47/*/*/ || $code === 92/*\*/); + $code = $path[$len - 1]; + $trailingSeparator = ($code === '/' || $code === '\\'); if ($rootEnd < $len) { $tail = static::normalizeStringWin32(substr($path, $rootEnd), !$isAbsolute); } else { @@ -1138,8 +1138,8 @@ protected static function posix_normalize($path) if ($path === '') { return '.'; } - $isAbsolute = ord($path[0]) === 47/*/*/; - $trailingSeparator = ord($path[strlen($path) - 1]) === 47/*/*/; + $isAbsolute = $path[0] === '/'; + $trailingSeparator = $path[strlen($path) - 1] === '/'; // Normalize the path $path = static::normalizeStringPosix($path, !$isAbsolute); diff --git a/src/PreviousMap.php b/src/PreviousMap.php index 41a5ea3..c7c2ed7 100644 --- a/src/PreviousMap.php +++ b/src/PreviousMap.php @@ -2,9 +2,9 @@ namespace PostCSS; -use PostCSS\SourceMap\Consumer\Consumer as MozillaSourceMapConsumer; -use PostCSS\SourceMap\Generator as MozillaSourceMapGenerator; use PostCSS\Path\NodeJS as Path; +use PostCSS\SourceMap\Consumer\Consumer as SourceMapConsumer; +use PostCSS\SourceMap\Generator as SourceMapGenerator; /** * Source map information from input CSS. @@ -13,6 +13,10 @@ * This class will automatically find source map in input CSS or in file system near input file (according `from` option). * * @link https://github.com/postcss/postcss/blob/master/lib/previous-map.es6 + * + * @example + * $root = \PostCSS\Parser::parse($css, ['from' => 'a.sass.css']); + * $root->input->map //=> PreviousMap */ class PreviousMap { @@ -22,6 +26,8 @@ class PreviousMap public $annotation = ''; /** + * Was source map inlined by data-uri to input CSS. + * * @var bool */ public $inline; @@ -32,13 +38,13 @@ class PreviousMap public $text = null; /** - * @var MozillaSourceMapConsumer|null + * @var SourceMapConsumer|null */ private $consumerCache = null; /** * @param string $css Input CSS source - * @param processOptions $opts {@link Processor#process} options + * @param array $opts Options */ public function __construct($css, array $opts) { @@ -56,12 +62,12 @@ public function __construct($css, array $opts) * * It is lazy method, so it will create object only on first call and then it will use cache. * - * @return MozillaSourceMapConsumer object woth source map information + * @return SourceMapConsumer object with source map information */ public function consumer() { if ($this->consumerCache === null) { - $this->consumerCache = MozillaSourceMapConsumer::construct($this->text); + $this->consumerCache = SourceMapConsumer::construct($this->text); } return $this->consumerCache; @@ -70,7 +76,7 @@ public function consumer() /** * Does source map contains `sourcesContent` with input source text. * - * @return {boolean} Is `sourcesContent` present + * @return bool Is `sourcesContent` present */ public function withContent() { @@ -79,11 +85,20 @@ public function withContent() return !empty($sc); } + /** + * @param string $string + * @param string $start + * + * @return bool + */ public function startWith($string, $start) { return is_string($string) && $string !== '' && substr($string, 0, strlen($start)) === $start; } + /** + * @param string $css + */ public function loadAnnotation($css) { if (preg_match('/\/\*\s*# sourceMappingURL=(.*)\s*\*\//', $css, $match)) { @@ -91,6 +106,13 @@ public function loadAnnotation($css) } } + /** + * @param string $text + * + * @throws Exception\UnsupportedSourceMapEncoding + * + * @return string + */ public function decodeInline($text) { $utfd64 = 'data:application/json;charset=utf-8;base64,'; @@ -116,6 +138,15 @@ public function decodeInline($text) } } + /** + * @param string $file + * @param mixed $prev + * + * @throws Exception\UnableToLoadPreviousSourceMap + * @throws Exception\UnsupportedPreviousSourceMapFormat + * + * @return string|false + */ public function loadMap($file, $prev) { if ($prev === false) { @@ -132,9 +163,9 @@ public function loadMap($file, $prev) } else { throw new Exception\UnableToLoadPreviousSourceMap($prevPath); } - } elseif ($prev instanceof MozillaSourceMapConsumer) { - return (string) MozillaSourceMapGenerator::fromSourceMap($prev); - } elseif ($prev instanceof MozillaSourceMapGenerator) { + } elseif ($prev instanceof SourceMapConsumer) { + return (string) SourceMapGenerator::fromSourceMap($prev); + } elseif ($prev instanceof SourceMapGenerator) { return (string) $prev; } elseif ($this->isMap($prev)) { return json_encode($prev); @@ -157,6 +188,11 @@ public function loadMap($file, $prev) } } + /** + * @param array|mixed $map + * + * @return bool + */ public function isMap($map) { $result = false; diff --git a/src/Processor.php b/src/Processor.php index 739d1d4..3264157 100644 --- a/src/Processor.php +++ b/src/Processor.php @@ -9,6 +9,11 @@ * * @link https://github.com/postcss/postcss/blob/master/lib/processor.es6 * @link https://github.com/postcss/postcss/blob/master/lib/postcss.es6 + * + * @example + * $processor = new \PostCSS\Processor([Autoprefixer::class, Precss::class]); + * $processor->process($css1)->then(function ($result) { echo $result->css; })->done(); + * $processor->process($css2)->then(function ($result) { echo $result->css; })->done(); */ class Processor { @@ -25,7 +30,7 @@ class Processor public $plugins; /** - * @param PluginInterface[]|callable[]|Processor[]} $plugins PostCSS plugins + * @param PluginInterface[]|callable[]|Processor[]} $plugins PostCSS plugins. Processor->usePlugin for plugin format */ public function __construct($plugins = []) { @@ -40,31 +45,24 @@ public function __construct($plugins = []) /** * Adds a plugin to be used as a CSS processor. * - * PostCSS plugin can be in 4 formats: - * * A plugin created by {@link postcss.plugin} method. - * * A function. PostCSS will pass the function a {@link Root} - * as the first argument and current {@link Result} instance - * as the second. - * * An object with a `postcss` method. PostCSS will use that method - * as described in #2. - * * Another {@link Processor} instance. PostCSS will copy plugins - * from that instance into this one. + * PostCSS plugin can be in the following formats: + * - An \PostCSS\PluginInterface instance. + * - The name of a \PostCSS\PluginInterface class. + * - A callable that returns a \PostCSS\PluginInterface instance. + * - Another Processor} instance. PostCSS will copy plugins from that instance into this one. * - * Plugins can also be added by passing them as arguments when creating - * a `postcss` instance (see [`postcss(plugins)`]). + * Plugins can also be added by passing them as arguments when creating \PostCSS\Processor instance. * * Asynchronous plugins should return a `\React\Promise\Promise` instance. * - * @param {Plugin|pluginFunction|Processor} plugin - PostCSS plugin - * or {@link Processor} - * with plugins + * @param PluginInterface|string|callable|Processor $plugin PostCSS plugin Processor with plugins * * @example - * const processor = postcss() - * .use(autoprefixer) - * .use(precss); + * $processor = (new \PostCSS\Processor()) + * ->usePlugin(Autoprefixer::class) + * ->usePlugin(Precss::class); * - * @return {Processes} current processor to make methods chain + * @return static Current processor to make methods chain */ public function usePlugin($plugin) { @@ -74,26 +72,17 @@ public function usePlugin($plugin) } /** - * Parses source CSS and returns a {@link LazyResult} Promise proxy. - * Because some plugins can be asynchronous it doesn't make - * any transformations. Transformations will be applied - * in the {@link LazyResult} methods. + * Parses source CSS and returns a LazyResult Promise proxy. + * Because some plugins can be asynchronous it doesn't make any transformations. + * Transformations will be applied in the LazyResult methods. * - * @param {string|toString|Result} css - String with input CSS or - * any object with a `toString()` - * method, like a Buffer. - * Optionally, send a {@link Result} - * instance and the processor will - * take the {@link Root} from it - * @param {processOptions} [opts] - options + * @param string|Root|Result|LazyResult $css String with input CSS or any object with a `__toString()` method. Optionally, send a Result instance and the processor will take the Root from it + * @param array $opts] Options * * @return LazyResult \React\Promise\Promise proxy * * @example - * processor.process(css, { from: 'a.css', to: 'a.out.css' }) - * .then(result => { - * console.log(result.css); - * }); + * $processor->process($css, ['from' => 'a.css', 'to' => 'a.out.css'])->then(function ($result) { echo $result->css; })->done(); */ public function process($css, array $opts = []) { diff --git a/src/Result.php b/src/Result.php index a1dd053..a571c69 100644 --- a/src/Result.php +++ b/src/Result.php @@ -6,48 +6,76 @@ /** * Provides the result of the PostCSS transformations. - * A Result instance is returned by {@link LazyResult#then} or {@link Root#toResult} methods. + * A Result instance is returned by LazyResult->then or Root->toResult methods. * * @link https://github.com/postcss/postcss/blob/master/lib/result.es6 * * @example - * postcss([cssnext]).process(css).then(function (result) { - * console.log(result.css); + * (new \PostCSS\Processor([cssnext]))->process($css)->then(function ($result) { + * echo $result->css; * }); * @example - * var result2 = postcss.parse(css).toResult(); + * $result2 = \PostCSS\Parser::parse($css).toResult(); * * @property string $content An alias for the Result->css property. Use it with syntaxes that generate non-CSS output */ class Result { /** + * The Processor instance used for this transformation. + * * @var Processor */ public $processor; /** + * Contains messages from plugins (e.g., warnings or custom messages). Each message should have type and plugin properties. + * * @var array */ public $messages; /** + * Root node after all transformations. + * + * @example + * $root->toResult()->root === root; + * * @var Root|null */ public $root; /** + * Options from the Processor->process or Root->toResult} call that produced this Result instance. + * + * @example + * $root->toResult($opts)->opts == $opts; + * * @var array */ public $opts; /** - * @var string|null A CSS string representing of {@link Result#root} + * A CSS string representing of Result->root. + * + * @example + * \PostCSS\Parser::parse('a{}')->toResult()->css //=> 'a{}' + * + * @var string|null */ public $css; /** - * @var SourceMap\Generator|null An instance of `SourceMapGenerator` class from the `source-map` library, representing changes o the {@link Result#root} instance + * An instance of `SourceMapGenerator` class from the `source-map` library, representing changes o the Result->root instance. + * + * @example + * $result->map->toJSON() //=> ['version' => 3, 'file' => 'a.css', ...] + * @example + * if ($result->map) { + * file_put_contents($result->opts['to'].'.map', (string) $result->map); + * } + * + * @var SourceMap\Generator|null */ public $map; @@ -57,10 +85,9 @@ class Result public $lastPlugin = null; /** - * @param {Processor} processor - processor used for this transformation - * @param {Root} root - Root node after all transformations - * @param {processOptions} opts - options from the {@link Processor#process} - * or {@link Root#toResult} + * @param Processor $processor Processor used for this transformation + * @param Root $root Root node after all transformations + * @param array $opts Options from the Processor->process or Root->toResult */ public function __construct(Processor $processor = null, Root $root = null, array $opts = []) { @@ -73,9 +100,9 @@ public function __construct(Processor $processor = null, Root $root = null, arra } /** - * Returns for {@link Result#css} content. + * Returns for Result->css content. * - * @return string string representing of {@link Result#root} + * @return string string representing of Result->root */ public function __toString() { @@ -83,14 +110,16 @@ public function __toString() } /** - * Creates an instance of {@link Warning} and adds it to {@link Result#messages}. + * Creates an instance of {@link Warning} and adds it to Result->messages. + * + * @param string $text Warning message + * @param array $opts Warning options { * - * @param string $text - warning message - * @param array $opts - warning options - * @param Node opts.node - CSS node that caused the warning - * @param string opts.word - word in CSS source that caused the warning - * @param int opts.index - index in CSS node string that caused the warning - * @param string opts.plugin - name of the plugin that created this warning. {@link Result#warn} fills this property automatically + * @var Node $node CSS node that caused the warning + * @var string $word Word in CSS source that caused the warning + * @var int $index Index in CSS node string that caused the warning + * @var string $plugin Name of the plugin that created this warning. Result->warn fills this property automatically. + * } * * @return Warning created warning */ @@ -109,9 +138,14 @@ public function warn($text, array $opts = []) } /** - * Returns warnings from plugins. Filters {@link Warning} instances from {@link Result#messages}. + * Returns warnings from plugins. Filters Warning instances from Result->messages. + * + * @example + * foreach ($result->warnings() as warn) { + * echo (string) $warn; + * } * - * @return {Warning[]} warnings from plugins + * @return Warning[] warnings from plugins */ public function warnings() { diff --git a/src/Root.php b/src/Root.php index 0daf821..1729fcb 100644 --- a/src/Root.php +++ b/src/Root.php @@ -6,9 +6,17 @@ * Represents a CSS file and contains all its parsed nodes. * * @link https://github.com/postcss/postcss/blob/master/lib/root.es6 + * + * @example + * $root = \PostCSS\Parser::parse('a{color:black} b{z-index:2}'); + * $root->type //=> 'root' + * count($root->nodes) //=> 2 */ class Root extends Container { + /** + * @param array $defaults + */ public function __construct(array $defaults = []) { parent::__construct($defaults); @@ -18,6 +26,11 @@ public function __construct(array $defaults = []) } } + /** + * {@inheritdoc} + * + * @see Container::removeChild() + */ public function removeChild($child) { $child = $this->index($child); @@ -34,6 +47,11 @@ public function removeChild($child) return parent::removeChild($child); } + /** + * {@inheritdoc} + * + * @see Container::normalize() + */ public function normalize($child, $sample = null, $type = null) { $nodes = parent::normalize($child); @@ -65,9 +83,9 @@ public function normalize($child, $sample = null, $type = null) } /** - * Returns a {@link Result} instance representing the root's CSS. + * Returns a Result instance representing the root's CSS. * - * @param ProcessOptions $opts Options with only `to` and `map` keys + * @param array $opts Options with only `to` and `map` keys. Same values as LazyResult::__constructor * * @return Result Result with current root's CSS */ @@ -79,7 +97,7 @@ public function toResult(array $opts = []) } /** - * @deprecated Use Root#removeChild + * @deprecated Use Root->removeChild */ public function remove() { @@ -92,7 +110,7 @@ public function remove() } /** - * @deprecated Use Root#source->input->map + * @deprecated Use Root->source->input->map */ public function prevMap() { diff --git a/src/Rule.php b/src/Rule.php index c5ce5c9..bc8f16e 100644 --- a/src/Rule.php +++ b/src/Rule.php @@ -6,14 +6,28 @@ * Represents a CSS rule: a selector followed by a declaration block. * * @link https://github.com/postcss/postcss/blob/master/lib/rule.es6 + * + * @example + * $root = \PostCSS\Parser::parse('a{}'); + * $rule = $root->first; + * $rule->type //=> 'rule' + * (string) $rule //=> 'a{}' + * + * @property string[] $selectors An array containing the rule's individual selectors. Groups of selectors are split at commas + * @property @deprecated string $_selector Rule->_selector is deprecated. Use Rule->raws->selector */ class Rule extends Container { /** + * The rule's full selector represented as a string. + * * @var string|null */ public $selector = null; + /** + * @param array $defaults + */ public function __construct(array $defaults = []) { $selectors = null; @@ -27,41 +41,38 @@ public function __construct(array $defaults = []) $this->nodes = []; } if ($selectors !== null) { - $this->setSelectors($selectors); + $this->selectors = $selectors; } } - /** - * An array containing the rule's individual selectors. - * Groups of selectors are split at commas. - */ - public function getSelectors() - { - return ListUtil::comma($this->selector); - } - - public function setSelectors(array $values) + public function __get($name) { - if (!isset($this->selector) || !$this->selector || !preg_match('/,\s*/', $this->selector, $match)) { - $match = null; + switch ($name) { + case 'selectors': + return ListUtil::comma($this->selector); + case '_selector': + return $this->raws->selector; + default: + return parent::__get($name); } - $sep = ($match !== null) ? $match[0] : ','.$this->raw('between', 'beforeOpen'); - $this->selector = implode($sep, $values); } - /** - * @deprecated Rule#_selector is deprecated. Use Rule#raws.selector - */ - public function _getSelector() - { - return $this->raws->selector; - } - - /** - * @deprecated Rule#_selector is deprecated. Use Rule#raws.selector - */ - public function _setSelector($val) + public function __set($name, $value) { - $this->raws->selector = $val; + switch ($name) { + case 'selectors': + if (!$this->selector || !preg_match('/,\s*/', $this->selector, $match)) { + $match = null; + } + $sep = ($match !== null) ? $match[0] : ','.$this->raw('between', 'beforeOpen'); + $this->selector = implode($sep, (array) $value); + break; + case '_selector': + $this->raws->selector = $value; + break; + default: + parent::__set($name, $value); + break; + } } } diff --git a/src/Stringifier.php b/src/Stringifier.php index fdf9c70..3435d60 100644 --- a/src/Stringifier.php +++ b/src/Stringifier.php @@ -32,6 +32,11 @@ class Stringifier 'commentRight' => ' ', ]; + /** + * @param string $str + * + * @return string + */ protected static function capitalize($str) { $str = (string) $str; @@ -46,12 +51,20 @@ protected static function capitalize($str) return $str; } + /** + * @param callable|null $builder + * @param callable|null $stringifier + */ public function __construct(callable $builder = null, callable $stringifier = null) { $this->builder = $builder; $this->stringifier = $stringifier; } + /** + * @param Node $node + * @param bool $semicolon + */ public function stringify(Node $node, $semicolon = null) { if ($this->stringifier !== null) { @@ -63,6 +76,9 @@ public function stringify(Node $node, $semicolon = null) } } + /** + * @param Node $node + */ protected function root(Node $node) { $this->body($node); @@ -72,6 +88,9 @@ protected function root(Node $node) } } + /** + * @param Node $node + */ protected function comment(Node $node) { $left = $this->raw($node, 'left', 'commentLeft'); @@ -79,6 +98,10 @@ protected function comment(Node $node) call_user_func($this->builder, '/*'.$left.$node->text.$right.'*/', $node); } + /** + * @param Node $node + * @param bool $semicolon + */ protected function decl(Node $node, $semicolon) { $between = $this->raw($node, 'between', 'colon'); @@ -92,11 +115,18 @@ protected function decl(Node $node, $semicolon) call_user_func($this->builder, $string, $node); } + /** + * @param Node $node + */ protected function rule(Node $node) { $this->block($node, $this->rawValue($node, 'selector')); } + /** + * @param Node $node + * @param bool $semicolon + */ protected function atrule(Node $node, $semicolon) { $name = '@'.$node->name; @@ -116,6 +146,9 @@ protected function atrule(Node $node, $semicolon) } } + /** + * @param Node $node + */ protected function body(Node $node) { $nodeNodesCount = count($node->nodes); @@ -137,6 +170,10 @@ protected function body(Node $node) } } + /** + * @param Node $node + * @param string $start + */ protected function block(Node $node, $start) { $between = $this->raw($node, 'between', 'beforeOpen'); @@ -154,6 +191,13 @@ protected function block(Node $node, $start) call_user_func($this->builder, '}', $node, 'end'); } + /** + * @param Node $node + * @param string $own + * @param string|null $detect + * + * @return mixed + */ public function raw(Node $node, $own, $detect = null) { if ($detect === null) { @@ -215,6 +259,11 @@ public function raw(Node $node, $own, $detect = null) return $value; } + /** + * @param Node $root + * + * @return bool|null + */ protected function rawSemicolon(Node $root) { $value = null; @@ -232,6 +281,11 @@ protected function rawSemicolon(Node $root) return $value; } + /** + * @param Node $root + * + * @return string|null + */ protected function rawEmptyBody(Node $root) { $value = null; @@ -249,6 +303,11 @@ protected function rawEmptyBody(Node $root) return $value; } + /** + * @param Node $root + * + * @return string|null + */ protected function rawIndent(Node $root) { $indent = $root->raws->indent; @@ -272,6 +331,12 @@ protected function rawIndent(Node $root) return $value; } + /** + * @param Container $root + * @param Node $node + * + * @return string|null + */ protected function rawBeforeComment(Container $root, Node $node) { $value = null; @@ -293,6 +358,12 @@ protected function rawBeforeComment(Container $root, Node $node) return $value; } + /** + * @param Container $root + * @param Node $node + * + * @return string|null + */ protected function rawBeforeDecl(Container $root, Node $node) { $value = null; @@ -314,6 +385,11 @@ protected function rawBeforeDecl(Container $root, Node $node) return $value; } + /** + * @param Container $root + * + * @return string|null + */ protected function rawBeforeRule(Container $root) { $value = null; @@ -334,6 +410,11 @@ protected function rawBeforeRule(Container $root) return $value; } + /** + * @param Container $root + * + * @return string|null + */ protected function rawBeforeClose(Container $root) { $value = null; @@ -354,6 +435,11 @@ protected function rawBeforeClose(Container $root) return $value; } + /** + * @param Container $root + * + * @return string|null + */ protected function rawBeforeOpen(Container $root) { $value = null; @@ -371,6 +457,11 @@ protected function rawBeforeOpen(Container $root) return $value; } + /** + * @param Container $root + * + * @return string|null + */ protected function rawColon(Container $root) { $value = null; @@ -386,6 +477,12 @@ protected function rawColon(Container $root) return $value; } + /** + * @param Node $node + * @param string $detect + * + * @return string + */ protected function beforeAfter(Node $node, $detect) { if ($node->type === 'decl') { @@ -417,6 +514,12 @@ protected function beforeAfter(Node $node, $detect) return $value; } + /** + * @param Node $node + * @param string $prop + * + * @return mixed|null + */ public function rawValue(Node $node, $prop) { $value = isset($node->$prop) ? $node->$prop : null; diff --git a/src/Warning.php b/src/Warning.php index 9369825..8c95314 100644 --- a/src/Warning.php +++ b/src/Warning.php @@ -3,13 +3,13 @@ namespace PostCSS; /** - * Represents a plugin's warning. It can be created using {@link Node#warn}. + * Represents a plugin's warning. It can be created using Node->warn. * * @link https://github.com/postcss/postcss/blob/master/lib/warning.es6 * * @example - * if ( decl.important ) { - * decl.warn(result, 'Avoid !important', { word: '!important' }); + * if ($decl->important ) { + * $decl->warn($result, 'Avoid !important', ['word' => '!important']); * } */ class Warning @@ -17,7 +17,7 @@ class Warning /** * The warning message. * - * @var + * @var string */ public $text; @@ -43,23 +43,35 @@ class Warning public $node = null; /** - * The name of the plugin that created it will fill this property automatically. this warning. When you call {@link Node#warn}. + * The name of the plugin that created it will fill this property automatically when you call Node->warn. * * @var string|null */ public $plugin = null; + /** + * Index in CSS node string that caused the warning. + * + * @var int|null + */ public $index = null; + /** + * Word in CSS source that caused the warning. + * + * @var string|null + */ public $word = null; /** * @param string $text Warning message - * @param array $opts Warning options - * @param {Node} opts.node CSS node that caused the warning - * @param string $opts.word Word in CSS source that caused the warning - * @param {number} opts.index Index in CSS node string that caused the warning - * @param string $opts.plugin Name of the plugin that created this warning. {@link Result#warn} fills this property automatically + * @param array $opts Warning options. { + * + * @var Node $node CSS node that caused the warning + * @var string $word Word in CSS source that caused the warning + * @var int $index Index in CSS node string that caused the warning + * @var string $plugin Name of the plugin that created this warning. Result->warn fills this property automatically. + * } */ public function __construct($text, array $opts = []) { diff --git a/test/tests/CssSyntaxErrorTest.php b/test/tests/CssSyntaxErrorTest.php index 83d0793..a107e26 100644 --- a/test/tests/CssSyntaxErrorTest.php +++ b/test/tests/CssSyntaxErrorTest.php @@ -16,7 +16,7 @@ class CssSyntaxErrorTest extends \PHPUnit_Framework_TestCase { /** * @param string $css - * @param array $opts + * @param array $opts * * @return CssSyntaxError|null */ diff --git a/test/tests/Helpers/DeletableDirectoryTest.php b/test/tests/Helpers/DeletableDirectoryTest.php index 58ffaec..50ba280 100644 --- a/test/tests/Helpers/DeletableDirectoryTest.php +++ b/test/tests/Helpers/DeletableDirectoryTest.php @@ -2,17 +2,8 @@ namespace PostCSS\Tests\Helpers; -use Exception; - -abstract class DeletableDirectoryTest extends \PHPUnit_Framework_TestCase +abstract class DeletableDirectoryTest extends FilesystemTest { - /** - * Return the name (without path) of the temporary directory to create/delete. - * - * @return string - */ - abstract protected function getDirectoryName(); - protected function tearDown() { parent::tearDown(); @@ -21,27 +12,10 @@ protected function tearDown() self::deleteDirectory($dir); } if (file_exists($dir)) { - throw new Exception("Failed to delete directory: $dir"); + throw new DirectoryRemovalError($dir); } } - /** - * Return the full path of the temporary directory to create/delete. - * - * @return string - */ - protected function getDirectoryPath() - { - return implode( - DIRECTORY_SEPARATOR, - [ - dirname(dirname(__DIR__)), - 'tmp', - $this->getDirectoryName(), - ] - ); - } - /** * Recusrively delete a directory. * @@ -86,50 +60,4 @@ private static function deleteDirectory($dir) return $result; } - - protected function getAbsoluteFilePath($name) - { - return $this->getDirectoryPath().DIRECTORY_SEPARATOR.trim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $name), DIRECTORY_SEPARATOR); - } - - /** - * @param string $name - * @param string $contents - * - * @return string - */ - protected function createRelativeFile($name, $contents) - { - $path = $this->getAbsoluteFilePath($name); - $this->createAbsoluteFile($path, $contents); - - return $path; - } - - /** - * @param string $path - * @param string $contents - */ - protected function createAbsoluteFile($path, $contents) - { - $p = strrpos($path, DIRECTORY_SEPARATOR); - $subDir = null; - if ($p !== false) { - $subDir = substr($path, 0, $p); - if (!is_dir($subDir)) { - @mkdir($subDir, 0777, true); - if (!is_dir($subDir)) { - $this->markTestSkipped("Failed to create directory: $subDir"); - } - } - } - if (@file_put_contents($path, $contents) === false) { - if ($subDir === null) { - $msg = "Failed to create file: $path"; - } else { - $msg = "Failed to create file $path (detected directory: $subDir)"; - } - $this->markTestSkipped($msg); - } - } } diff --git a/test/tests/Helpers/DirectoryCreationError.php b/test/tests/Helpers/DirectoryCreationError.php new file mode 100644 index 0000000..ffe19ab --- /dev/null +++ b/test/tests/Helpers/DirectoryCreationError.php @@ -0,0 +1,12 @@ +path = $path; + } +} diff --git a/test/tests/Helpers/DirectoryRemovalError.php b/test/tests/Helpers/DirectoryRemovalError.php new file mode 100644 index 0000000..716b15b --- /dev/null +++ b/test/tests/Helpers/DirectoryRemovalError.php @@ -0,0 +1,12 @@ +path = $path; + } +} diff --git a/test/tests/Helpers/FileCreationError.php b/test/tests/Helpers/FileCreationError.php new file mode 100644 index 0000000..4b7a68f --- /dev/null +++ b/test/tests/Helpers/FileCreationError.php @@ -0,0 +1,12 @@ +path = $path; + } +} diff --git a/test/tests/Helpers/FilesystemError.php b/test/tests/Helpers/FilesystemError.php new file mode 100644 index 0000000..1f228d5 --- /dev/null +++ b/test/tests/Helpers/FilesystemError.php @@ -0,0 +1,13 @@ +path; + } +} diff --git a/test/tests/Helpers/FilesystemTest.php b/test/tests/Helpers/FilesystemTest.php new file mode 100644 index 0000000..ed99024 --- /dev/null +++ b/test/tests/Helpers/FilesystemTest.php @@ -0,0 +1,74 @@ +getDirectoryName(), + ] + ); + } + + protected function getAbsoluteFilePath($name) + { + return $this->getDirectoryPath().DIRECTORY_SEPARATOR.trim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $name), DIRECTORY_SEPARATOR); + } + + /** + * @param string $name + * @param string $contents + * + * @throws FilesystemError + * + * @return string + */ + protected function createRelativeFile($name, $contents) + { + $path = $this->getAbsoluteFilePath($name); + $this->createAbsoluteFile($path, $contents); + + return $path; + } + + /** + * @param string $path + * @param string $contents + * + * @throws FilesystemError + */ + protected function createAbsoluteFile($path, $contents) + { + $p = strrpos($path, DIRECTORY_SEPARATOR); + if ($p !== false) { + $subDir = substr($path, 0, $p); + if (!is_dir($subDir)) { + @mkdir($subDir, 0777, true); + if (!is_dir($subDir)) { + throw new DirectoryCreationError($subDir); + } + } + } + if (@file_put_contents($path, $contents) === false) { + throw new FileCreationError($path); + } + } +} diff --git a/test/tests/Helpers/InstantiableNode.php b/test/tests/Helpers/InstantiableNode.php new file mode 100644 index 0000000..287ba94 --- /dev/null +++ b/test/tests/Helpers/InstantiableNode.php @@ -0,0 +1,9 @@ +getAbsoluteFilePath($cacheName); + if (is_file($cachePath)) { + $result = @file_get_contents($cachePath); + if ($result !== false) { + return $result; + } + } $headers = @get_headers($url); if (empty($headers)) { throw new DownloadError("Failed to get headers of $url"); @@ -54,6 +81,11 @@ private static function getRemoteContents($url, $parentUrl = null) switch ($contentEncoding) { } + try { + $this->createAbsoluteFile($cachePath, $data); + } catch (FilesystemError $x) { + } + return $data; } @@ -65,9 +97,9 @@ public function testReal($name, $url) try { $resourceUrls = []; if (substr($url, -4) === '.css') { - $resourceUrls[$url] = self::getRemoteContents($url); + $resourceUrls[$url] = $this->getRemoteContents($url); } else { - $contents = self::getRemoteContents($url); + $contents = $this->getRemoteContents($url); if (!preg_match_all('/[^"]+\.css"|[^\']+\.css\'/', $contents, $m)) { throw new Exception("Can't find CSS links at $url"); } @@ -86,7 +118,7 @@ function ($path) use ($url) { $m[0] ); foreach ($files as $file) { - $resourceUrls[$file] = self::getRemoteContents($file); + $resourceUrls[$file] = $this->getRemoteContents($file); } } diff --git a/test/tests/MapTest.php b/test/tests/MapTest.php index 0bd2870..351a4bd 100644 --- a/test/tests/MapTest.php +++ b/test/tests/MapTest.php @@ -283,7 +283,11 @@ public function testMissesCheckFilesOnRequires() 'map' => true, ]); - $this->createAbsoluteFile($file.'.map', $step1->map); + try { + $this->createAbsoluteFile($file.'.map', $step1->map); + } catch (Helpers\FilesystemError $x) { + $this->markTestSkipped($x->getMessage()); + } $step2 = static::lighter()->process($step1->css, [ 'from' => $file, 'to' => 'b.css', @@ -364,7 +368,11 @@ public function testUsesMapFromSubdirIfItWrittenAsAFile() $source = static::consumer($step1->map)->originalPositionFor(['line' => 1, 'column' => 0])['source']; $this->assertSame('../../source/a.css', $source); - $file = $this->createRelativeFile('one/maps/b.css.map', $step1->map); + try { + $file = $this->createRelativeFile('one/maps/b.css.map', $step1->map); + } catch (Helpers\FilesystemError $x) { + $this->markTestSkipped($x->getMessage()); + } $step2 = static::doubler()->process($step1->css, [ 'from' => $this->getAbsoluteFilePath('one/b.css'), 'to' => $this->getAbsoluteFilePath('two/c.css'), diff --git a/test/tests/NodeTest.php b/test/tests/NodeTest.php index a74a7c9..93b3ef4 100644 --- a/test/tests/NodeTest.php +++ b/test/tests/NodeTest.php @@ -13,7 +13,6 @@ use PostCSS\Parser; use PostCSS\Processor; use PostCSS\Warning; -use React\Promise\FulfilledPromise; class NodeTest extends \PHPUnit_Framework_TestCase { @@ -70,17 +69,18 @@ function ($css, $result) use (&$warning) { ); $me = $this; - $result = (new Processor([$warner]))->process('a{}')->then( - function ($result) use ($me, &$warning) { - $me->assertInstanceOf(Warning::class, $warning); - $me->assertSame('FIRST!', $warning->text); - $me->assertSame('warner', $warning->plugin); - $me->assertSame([$warning], $result->warnings()); - }, - function ($error) { - } - ); - $this->assertInstanceOf(FulfilledPromise::class, $result); + (new Processor([$warner]))->process('a{}') + ->then( + function ($result) use ($me, &$warning) { + $me->assertInstanceOf(Warning::class, $warning); + $me->assertSame('FIRST!', $warning->text); + $me->assertSame('warner', $warning->plugin); + $me->assertSame([$warning], $result->warnings()); + }, + function ($error) { + } + ) + ->done(); } public function testWarnAcceptsOptions() diff --git a/test/tests/PostCSSTest.php b/test/tests/PostCSSTest.php index 96571e4..3b31b74 100644 --- a/test/tests/PostCSSTest.php +++ b/test/tests/PostCSSTest.php @@ -4,7 +4,6 @@ use PostCSS\Processor; use PostCSS\Plugin\ClosurePlugin; -use React\Promise\FulfilledPromise; use PostCSS\Parser; use PostCSS\Root; use PostCSS\Comment; @@ -150,14 +149,15 @@ function ($str = 'bar') { $this->assertSame('a{value:baz}', $result2->css); $me = $this; - $cr = $plugin->process('a{value:foo}', [])->then( - function ($result) use ($me) { - $me->assertSame('a{value:bar}', $result->css); - }, - function () { - } - ); - $this->assertInstanceOf(FulfilledPromise::class, $cr); + $plugin->process('a{value:foo}', []) + ->then( + function ($result) use ($me) { + $me->assertSame('a{value:bar}', $result->css); + }, + function () { + } + ) + ->done(); } public function testContainsParser() diff --git a/test/tests/PreviousMapTest.php b/test/tests/PreviousMapTest.php index 1e48ee5..7f9dc94 100644 --- a/test/tests/PreviousMapTest.php +++ b/test/tests/PreviousMapTest.php @@ -142,7 +142,11 @@ public function testRaisesOnUnknownMapFormat() public function testReadsMapFromAnnotation() { - $file = $this->createRelativeFile('a.map', static::getMap()); + try { + $file = $this->createRelativeFile('a.map', static::getMap()); + } catch (Helpers\FilesystemError $x) { + $this->markTestSkipped($x->getMessage()); + } $root = Parser::parse("a{}\n/*# sourceMappingURL=a.map */", ['from' => $file]); $this->assertSame(static::getMap(), $root->source['input']->map->text); @@ -180,7 +184,11 @@ public function testShouldAcceptAnEmptyMappingsString() public function testShouldAcceptAFunction() { $css = "body{}\n/*# sourceMappingURL=a.map */"; - $file = $this->createRelativeFile('previous-sourcemap-function.map', static::getMap()); + try { + $file = $this->createRelativeFile('previous-sourcemap-function.map', static::getMap()); + } catch (Helpers\FilesystemError $x) { + $this->markTestSkipped($x->getMessage()); + } $opts = [ 'map' => [ 'prev' => function (/* from */) use ($file) { @@ -195,24 +203,25 @@ public function testShouldAcceptAFunction() public function testShouldCallFunctionWithOptsFrom() { - $numberOfAssertions = 0; - $css = "body{}\n/*# sourceMappingURL=a.map */"; - $file = $this->createRelativeFile('previous-sourcemap-function.map', static::getMap()); + try { + $file = $this->createRelativeFile('previous-sourcemap-function.map', static::getMap()); + } catch (Helpers\FilesystemError $x) { + $this->markTestSkipped($x->getMessage()); + } $me = $this; $opts = [ 'from' => 'a.css', 'map' => [ - 'prev' => function ($from) use ($me, $file, &$numberOfAssertions) { + 'prev' => function ($from) use ($me, $file) { $me->assertSame('a.css', $from); - ++$numberOfAssertions; return $file; }, ], ]; Parser::parse($css, $opts); - $this->assertSame(1, $numberOfAssertions); + $this->assertSame(1, static::getCount()); } public function testShouldRaiseWhenFunctionReturnsInvalidPath() diff --git a/test/tests/ProcessorTest.php b/test/tests/ProcessorTest.php index a18059f..0365b3d 100644 --- a/test/tests/ProcessorTest.php +++ b/test/tests/ProcessorTest.php @@ -3,14 +3,14 @@ namespace PostCSS\Tests; use PostCSS\Exception\CssSyntaxError; +use PostCSS\LazyResult; +use PostCSS\Node; use PostCSS\Parser; +use PostCSS\Path\NodeJS as Path; use PostCSS\Plugin\ClosurePlugin; use PostCSS\Processor; -use PostCSS\Node; -use PostCSS\Root; -use PostCSS\Path\NodeJS as Path; -use PostCSS\LazyResult; use PostCSS\Result; +use PostCSS\Root; class ProcessorTest extends \PHPUnit_Framework_TestCase { @@ -235,54 +235,46 @@ public function testCallsAllPluginsOnce() $calls .= 'b'; }); - $assertions = 0; $me = $this; - $result = (new Processor([$a, $b])) + (new Processor([$a, $b])) ->process('') ->then( - function () use ($me, &$calls, &$assertions) { + function () use ($me, &$calls) { $me->assertSame('ab', $calls); - ++$assertions; }, function () { } - ); - $this->assertSame(1, $assertions); + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testParsesConvertsAndStringifiesCSS() { - $assertions = 0; $me = $this; $a = new ClosurePlugin( - function ($css) use ($me, &$assertions) { + function ($css) use ($me) { $me->assertInstanceOf(Root::class, $css); - ++$assertions; } ); $this->assertInternalType('string', (new Processor([$a]))->process('a {}')->css); - $this->assertSame(1, $assertions); + $this->assertSame(2, static::getCount()); } public function testSendResultToPlugins() { $processor = new Processor(); - $assertions = 0; $me = $this; $a = new ClosurePlugin( - function ($css, $result) use ($me, &$assertions, $processor) { + function ($css, $result) use ($me, $processor) { $me->assertInstanceOf(Result::class, $result); - ++$assertions; $me->assertSame($processor, $result->processor); - ++$assertions; $me->assertSame(['map' => true], $result->opts); - ++$assertions; $me->assertSame($css, $result->root); - ++$assertions; } ); $processor->usePlugin($a)->process('a {}', ['map' => true])->css; - $this->assertSame(4, $assertions); + $this->assertSame(4, static::getCount()); } public function testAcceptsSourceMapFromPostCSS() @@ -309,156 +301,152 @@ public function testAcceptsSourceMapFromPostCSS() public function testWorksAsyncWithoutPlugins() { $me = $this; - $assertions = 0; - - return (new Processor())->process('a {}')->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('a {}', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + (new Processor())->process('a {}') + ->then( + function ($result) use ($me) { + $me->assertSame('a {}', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testSetsLastPluginToResult() { - $assertions = 0; $me = $this; - $plugin1 = new ClosurePlugin(function ($css, $result) use ($me, &$assertions, &$plugin1) { + $plugin1 = new ClosurePlugin(function ($css, $result) use ($me, &$plugin1) { $me->assertSame($result->lastPlugin, $plugin1); - ++$assertions; }); - $plugin2 = new ClosurePlugin(function ($css, $result) use ($me, &$assertions, &$plugin2) { + $plugin2 = new ClosurePlugin(function ($css, $result) use ($me, &$plugin2) { $me->assertSame($result->lastPlugin, $plugin2); - ++$assertions; }); $processor = new Processor([$plugin1, $plugin2]); - $processor->process('a{}')->then( - function ($result) use ($me, &$assertions, $plugin2) { - $me->assertSame($result->lastPlugin, $plugin2); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(3, $assertions); + $processor->process('a{}') + ->then( + function ($result) use ($me, $plugin2) { + $me->assertSame($result->lastPlugin, $plugin2); + }, + function () { + } + ) + ->done(); + $this->assertSame(3, static::getCount()); } public function testUsesCustomParsers() { - $assertions = 0; $me = $this; $processor = new Processor([]); $processor->process( 'a{}', ['parser' => [self::class, 'prs']] - )->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('ok', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ) + ->then( + function ($result) use ($me) { + $me->assertSame('ok', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testUsesCustomParsersFromObject() { $me = $this; - $assertions = 0; $processor = new Processor([]); $syntax = ['parse' => [self::class, 'prs'], 'stringify' => [self::class, 'str']]; $processor->process( 'a{}', ['parser' => $syntax] - )->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('ok', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ) + ->then( + function ($result) use ($me) { + $me->assertSame('ok', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testUsesCustomStringifier() { $me = $this; - $assertions = 0; $processor = new Processor([]); $processor->process( 'a{}', ['stringifier' => [self::class, 'str']] - )->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('!', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ) + ->then( + function ($result) use ($me) { + $me->assertSame('!', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testUsesCustomStringifierFromObject() { $me = $this; - $assertions = 0; $processor = new Processor([]); $syntax = ['parse' => [self::class, 'prs'], 'stringify' => [self::class, 'str']]; $processor->process( '', ['stringifier' => $syntax] - )->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('!', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ) + ->then( + function ($result) use ($me) { + $me->assertSame('!', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testUsesCustomStringifierWithSourceMaps() { $me = $this; - $assertions = 0; $processor = new Processor([]); $processor->process('a{}', ['map' => true, 'stringifier' => [self::class, 'str']]) - ->then( - function ($result) use ($me, &$assertions) { - $me->assertRegExp('/!\n\/\*# sourceMap/', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ->then( + function ($result) use ($me) { + $me->assertRegExp('/!\n\/\*# sourceMap/', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } public function testUsesCustomSyntax() { $me = $this; - $assertions = 0; $processor = new Processor([]); $syntax = ['parse' => [self::class, 'prs'], 'stringify' => [self::class, 'str']]; - return $processor->process( + $processor->process( 'a{}', ['syntax' => $syntax] - )->then( - function ($result) use ($me, &$assertions) { - $me->assertSame('ok!', $result->css); - ++$assertions; - }, - function () { - } - ); - $this->assertSame(1, $assertions); + ) + ->then( + function ($result) use ($me) { + $me->assertSame('ok!', $result->css); + }, + function () { + } + ) + ->done(); + $this->assertSame(1, static::getCount()); } } diff --git a/test/tests/RuleTest.php b/test/tests/RuleTest.php index ef39820..647f591 100644 --- a/test/tests/RuleTest.php +++ b/test/tests/RuleTest.php @@ -16,13 +16,13 @@ public function testInitializesWithProperties() public function testReturnsArrayInSelectors() { $rule = new Rule(['selector' => 'a,b']); - $this->assertSame(['a', 'b'], $rule->getSelectors()); + $this->assertSame(['a', 'b'], $rule->selectors); } public function testTrimsSelectors() { $rule = new Rule(['selector' => ".a\n, .b , .c"]); - $this->assertSame(['.a', '.b', '.c'], $rule->getSelectors()); + $this->assertSame(['.a', '.b', '.c'], $rule->selectors); } public function testIsSmartAboutSelectorsCommas() @@ -32,35 +32,36 @@ public function testIsSmartAboutSelectorsCommas() ]); $this->assertSame( ['[foo=\'a, b\']', 'a:-moz-any(:focus, [href*=\',\'])'], - $rule->getSelectors() + $rule->selectors ); } public function testReceiveArrayInSelectors() { $rule = new Rule(['selector' => 'i, b']); - $rule->setSelectors(['em', 'strong']); + $rule->selectors = ['em', 'strong']; $this->assertSame('em, strong', $rule->selector); } public function testSavesSeparatorInSelectors() { $rule = new Rule(['selector' => "i,\nb"]); - $rule->setSelectors(['em', 'strong']); + $rule->selectors = ['em', 'strong']; $this->assertSame("em,\nstrong", $rule->selector); } public function testUsesBetweenToDetectSeparatorInSelectors() { $rule = new Rule(['selector' => 'b', 'raws' => ['between' => '']]); - $rule->setSelectors(['b', 'strong']); + $rule->selectors = ['b', 'strong']; $this->assertSame('b,strong', $rule->selector); } public function testUsesSpaceInSeparatorBeDefaultInSelectors() { $rule = new Rule(['selector' => 'b']); - $rule->setSelectors(['b', 'strong']); + $rule->selectors = ['b', 'strong']; + $rule->_selector = 1; $this->assertSame('b, strong', $rule->selector); } diff --git a/test/tests/StringifierTest.php b/test/tests/StringifierTest.php index 1320932..021566b 100644 --- a/test/tests/StringifierTest.php +++ b/test/tests/StringifierTest.php @@ -4,11 +4,11 @@ use PostCSS\AtRule; use PostCSS\Declaration; -use PostCSS\Node; +use PostCSS\Parser; use PostCSS\Root; use PostCSS\Rule; use PostCSS\Stringifier; -use PostCSS\Parser; +use PostCSS\Tests\Helpers\InstantiableNode; class StringifierTest extends \PHPUnit_Framework_TestCase { @@ -25,11 +25,11 @@ protected function setUp() public function testCreatesTrimmedRawProperty() { - $b = new Node(['one' => 'trim']); + $b = new InstantiableNode(['one' => 'trim']); $b->raws->one = ['value' => 'trim', 'raw' => 'raw']; $this->assertSame('raw', $this->str->rawValue($b, 'one')); - $b = new Node(['one' => 'trim']); + $b = new InstantiableNode(['one' => 'trim']); $b->raws->one = (object) ['value' => 'trim', 'raw' => 'raw']; $this->assertSame('raw', $this->str->rawValue($b, 'one')); @@ -39,7 +39,7 @@ public function testCreatesTrimmedRawProperty() public function testWorksWithoutRawValueMagic() { - $b = new Node(); + $b = new InstantiableNode(); $b->one = '1'; $this->assertSame('1', $b->one); $this->assertSame('1', $this->str->rawValue($b, 'one'));