diff --git a/src/Ast/Traverser.php b/src/Ast/Traverser.php index 5d351e8b..39c70f56 100755 --- a/src/Ast/Traverser.php +++ b/src/Ast/Traverser.php @@ -79,11 +79,6 @@ protected function process($node, array $data) protected function doTraverse($node, $level) { - if (isset($node->value) && is_array($node->value)) { - - $node->value = Value::renderTokens($node->value); - } - $result = $this->process($node, $this->emit('enter', $node, $level)); if ($result === static::IGNORE_NODE) { diff --git a/src/Property/Config.php b/src/Config.php similarity index 98% rename from src/Property/Config.php rename to src/Config.php index 0e5b3b24..524f180b 100755 --- a/src/Property/Config.php +++ b/src/Config.php @@ -1,8 +1,8 @@ parse(); @@ -120,7 +116,7 @@ public static function from($css, array $options = []) * @inheritDoc * @throws Exception */ - public static function fromUrl($url, array $options = []) + public static function fromUrl(string $url, array $options = []): ElementInterface { return (new Parser(options: $options))->load($url)->parse(); @@ -129,7 +125,7 @@ public static function fromUrl($url, array $options = []) /** * @inheritDoc */ - public function traverse(callable $fn, $event) { + public function traverse(callable $fn, string $event): ElementInterface { return (new Traverser())->on($event, $fn)->traverse($this); } @@ -139,7 +135,7 @@ public function traverse(callable $fn, $event) { * @inheritDoc * @throws Parser\SyntaxError */ - public function query($query): array { + public function query(string $query): array { return (new Evaluator())->evaluate($query, $this); } @@ -149,7 +145,7 @@ public function query($query): array { * @inheritDoc * @throws Parser\SyntaxError */ - public function queryByClassNames($query): array { + public function queryByClassNames(string $query): array { return (new Evaluator())->evaluateByClassName($query, $this); } @@ -157,7 +153,7 @@ public function queryByClassNames($query): array { /** * @inheritDoc */ - public function getRoot () { + public function getRoot (): ElementInterface { $element = $this; @@ -172,42 +168,8 @@ public function getRoot () { /** * @inheritDoc */ - public function getValue() { - - return $this->ast->value ?? null; - } - - /** - * @inheritDoc - */ - public function getRawValue(): ?array { - - return $this->rawValue; - } - - /** - * @inheritDoc - */ - public function setValue ($value) { - - if (!is_array($value)) { - - $this->rawValue = Value::parse($value, $this->ast->name ?? null); - } - - else { - - $this->rawValue = $value; - } - - $this->ast->value = Value::renderTokens($this->rawValue); - return $this; - } - - /** - * @inheritDoc - */ - public function getParent () { + public function getParent (): ?RuleListInterface + { return $this->parent; } @@ -215,7 +177,7 @@ public function getParent () { /** * @inheritDoc */ - public function getType() { + public function getType(): string { return $this->ast->type; } @@ -223,7 +185,7 @@ public function getType() { /** * @inheritDoc */ - public function copy() { + public function copy(): ElementInterface { $parent = $this; $copy = $node = clone $this; @@ -248,7 +210,7 @@ public function copy() { /** * @inheritDoc */ - public function getSrc() { + public function getSrc(): ?string { return $this->ast->src ?? null; } @@ -256,7 +218,7 @@ public function getSrc() { /** * @inheritDoc */ - public function getPosition() { + public function getPosition(): stdClass|null { return $this->ast->position ?? null; } @@ -328,7 +290,7 @@ public function getLeadingComments(): ?array { * @inheritDoc * @throws Exception */ - public function deduplicate(array $options = ['allow_duplicate_rules' => ['font-face']]) + public function deduplicate(array $options = ['allow_duplicate_rules' => ['font-face']]): ElementInterface { if ((empty($options['allow_duplicate_rules']) || @@ -340,24 +302,11 @@ public function deduplicate(array $options = ['allow_duplicate_rules' => ['font- $ast = $this->toObject(); $this->ast = $parser->deduplicate($ast); - -// if (!empty($ast->children)) { -// -// foreach ($ast->children as $key => $node) { -// -// $ast->children[$key] = Element::getInstance($node); -// } -// } } return $this; } - public function setAst(ElementInterface $element) { - - $this->ast = $element->getAst(); - } - public function getAst() { diff --git a/src/Element/AtRule.php b/src/Element/AtRule.php index 36a7786a..bcfc5ad2 100755 --- a/src/Element/AtRule.php +++ b/src/Element/AtRule.php @@ -3,15 +3,16 @@ namespace TBela\CSS\Element; use Exception; -use \TBela\CSS\Interfaces\ElementInterface; +use stdClass; +use TBela\CSS\Interfaces\AtRuleInterface; +use TBela\CSS\Interfaces\ElementInterface; +use TBela\CSS\Value; /** * Class AtRule * @package TBela\CSS\Element */ -class AtRule extends RuleSet { - - use ElementTrait; +class AtRule extends RuleSet implements AtRuleInterface { /** * Type of @at-rule that contains other rules like @media @@ -26,11 +27,104 @@ class AtRule extends RuleSet { */ const ELEMENT_AT_NO_LIST = 2; + /** + * get css node name + * @param bool $vendor_prefixed + * @return string + */ + public function getName(bool $vendor_prefixed = true): string { + + $vendor = $this->getVendor(); + + if ($vendor_prefixed && $vendor !== '') { + + return '-'.$vendor.'-'.$this->ast->name; + } + + return $this->ast->name; + } + + /** + * get node name + * @param string $name + * @return AtRule + */ + public function setName (string $name): static { + + $name = trim($name); + if (preg_match('/^(-([a-zA-Z]+)-(\S+))/', $name, $match)) { + + $this->ast->vendor = $match[2]; + $this->ast->name = Value::escape($match[3]); + } + + else { + + $this->ast->name = Value::escape($name); + } + + return $this; + } + + /** + * @param string|null $prefix + * @return $this + */ + public function setVendor (?string $prefix): static { + + if (is_null($prefix) || $prefix === '') { + + unset($this->ast->vendor); + } + + else { + + $this->ast->vendor = $prefix; + } + + return $this; + } + + /** + * set vendor prefix + * @return string + */ + public function getVendor() : string { + + if (isset($this->ast->vendor)) { + + return $this->ast->vendor; + } + + return ''; + } + + /** + * @throws Exception + */ + public function getValue(): ?string + { + + return $this->ast->value ?? null; + } + + /** + * @throws Exception + */ + public function setValue(string|array $value): static + { + $this->ast->value = is_array($value) ? Value::renderTokens($value) : $value; + $this->ast->tokens = is_array($value) ? $value : null; + + return $this; + } + /** * test if this at-rule node is a leaf * @return bool */ - public function isLeaf () { + public function isLeaf (): bool + { return !empty($this->ast->isLeaf); } @@ -86,7 +180,8 @@ public function support (ElementInterface $child): bool { * @return Declaration * @throws Exception */ - public function addDeclaration ($name, $value) { + public function addDeclaration (string $name, string $value): Declaration + { $declaration = new Declaration(); @@ -97,7 +192,7 @@ public function addDeclaration ($name, $value) { } /** - * @return \stdClass + * @return stdClass * @ignore */ public function jsonSerialize () { diff --git a/src/Element/Comment.php b/src/Element/Comment.php index 617f8bcf..d18b9607 100755 --- a/src/Element/Comment.php +++ b/src/Element/Comment.php @@ -10,4 +10,14 @@ */ class Comment extends Element { + public function getValue(): string { + + return $this->ast->value; + } + + public function setValue(string $value): static { + + $this->ast->value = $value; + return $this; + } } \ No newline at end of file diff --git a/src/Element/Declaration.php b/src/Element/Declaration.php index 53a28c03..630ec699 100755 --- a/src/Element/Declaration.php +++ b/src/Element/Declaration.php @@ -1,10 +1,114 @@ -getVendor(); + + return $vendor_prefixed && $vendor !== '' ? '-' . $vendor . '-' . $this->ast->name : $this->ast->name; + } + + /** + * get node name + * @param string $name + * @return Declaration + */ + public function setName(string $name): static + { + + $name = trim($name); + if (preg_match('/^(-([a-zA-Z]+)-(\S+))/', $name, $match)) { + + $this->ast->vendor = $match[2]; + $this->ast->name = Value::escape($match[3]); + } else { + + $this->ast->name = Value::escape($name); + } + + return $this; + } + + /** + * @param string|null $prefix + * @return $this + */ + public function setVendor(?string $prefix): static + { + + if (is_null($prefix) || $prefix === '') { + + unset($this->ast->vendor); + } else { + + $this->ast->vendor = $prefix; + } + + return $this; + } + + /** + * set vendor prefix + * @return string + */ + public function getVendor(): string + { + + if (isset($this->ast->vendor)) { + + return $this->ast->vendor; + } + + return ''; + } + + /** + * @throws \Exception + */ + public function getValue(): ?string + { + + if (!isset($this->ast->value) && isset($this->ast->tokens)) { + + $this->ast->value = Value::renderTokens($this->ast->tokens); + } + + return $this->ast->value ?? null; + } + + /** + * @throws \Exception + */ + public function setValue(string|array $value): static + { + $this->ast->value = is_array($value) ? Value::renderTokens($value) : $value; + $this->ast->tokens = is_array($value) ? $value : null; + + return $this; + } + + public function getTokens(): ?array + { + + if (isset($this->ast->value) && !isset($this->ast->tokens)) { + + $this->ast->tokens = Value::parse($this->ast->value, $this->ast->name); + } + + return $this->ast->tokens ?? null; + } } \ No newline at end of file diff --git a/src/Element/Declaration/AbstractDeclarationSet.php b/src/Element/Declaration/AbstractDeclarationSet.php new file mode 100644 index 00000000..e6dfdf23 --- /dev/null +++ b/src/Element/Declaration/AbstractDeclarationSet.php @@ -0,0 +1,58 @@ +add((object)[ + + 'type' => $propertyType ?? 'Declaration', + 'name' => $parseName['name'] ?? $name, + 'src' => $src, + 'value' => $value, + 'leadingcomments' => $leadingcomments, + 'trailingcomments' => $trailingcomments, + 'vendor' => $vendor ?? $parseName['vendor'] ?? null + ]); + } + + public function isEmpty(): bool + { + + return empty($this->properties); + } + + public function has($property): bool + { + + return isset($this->properties[$property]); + } + + public function remove($property): static + { + + unset($this->properties[$property]); + return $this; + } +} \ No newline at end of file diff --git a/src/Element/Declaration/ValueList.php b/src/Element/Declaration/ValueList.php new file mode 100755 index 00000000..f3922f21 --- /dev/null +++ b/src/Element/Declaration/ValueList.php @@ -0,0 +1,317 @@ + true, + 'allow_duplicate_declarations' => false + ]; + + /*** + * PropertyList constructor. + * @param RuleList|null $list + * @param array $options + */ + public function __construct(?RuleList $list = null, array $options = []) + { + + $this->options = array_merge($this->options, $options); + + if ((is_callable([$list, 'hasDeclarations']) && $list->hasDeclarations()) || $list instanceof Rule) { + + foreach ($list as $element) { + + $this->set($element['name'], $element['value'], $element['type'], $element['leadingcomments'], $element['trailingcomments']); + } + } + } + + public function has($property): bool + { + + if (isset($this->properties[$property])) { + + return true; + } + + $shorthand = Config::getProperty($property . '.shorthand'); + + if (!isset($shorthand)) { + + $shorthand = Config::getPath('map.' . $property . '.shorthand'); + } + + if (isset($shorthand) && isset($this->properties[$shorthand])) { + + return $this->properties[$shorthand]->has($property); + } + + return false; + } + + public function remove($property): static + { + if (isset($this->properties[$property])) { + + unset($this->properties[$property]); + } else { + + $shorthand = Config::getProperty($property . '.shorthand'); + + if (!isset($shorthand)) { + + $shorthand = Config::getPath('map.' . $property . '.shorthand'); + } + + if (isset($shorthand) && isset($this->properties[$shorthand])) { + + $this->properties[$shorthand]->remove($property); + + if ($this->properties[$shorthand]->isEmpty()) { + + unset($this->properties[$shorthand]); + } + } + } + + return $this; + } + + public function add($declaration): static + { + + if ($declaration->type == 'Comment') { + + $this->properties[] = $declaration; + return $this; + } + + if (!isset($declaration->tokens)) { + + $declaration->tokens = is_array($declaration->value) ? $declaration->value : Value::parse($declaration->value, $declaration->name); + } + + $name = $declaration->name ?? null; + $vendor = $declaration->vendor ?? null; + + $propertyName = is_null($vendor) ? $name : '-' . $vendor . '-' . $name; + + if (!empty($this->options['allow_duplicate_declarations'])) { + + if ($this->options['allow_duplicate_declarations'] === true || + (is_array($this->options['allow_duplicate_declarations']) && in_array($propertyName, $this->options['allow_duplicate_declarations']))) { + + $this->properties[] = $declaration; + return $this; + } + } + + unset($this->properties[$propertyName]); + + if (empty($this->options['compute_shorthand'])) { + + $this->properties[$propertyName] = $declaration; + return $this; + } + + $shorthand = Config::getProperty($propertyName . '.shorthand'); + + // is is a shorthand property? + if (!is_null($shorthand) && !is_null(Config::getProperty($shorthand))) { + + $config = Config::getProperty($shorthand); + + if (!isset($this->properties[$shorthand]) || (!($this->properties[$shorthand] instanceof ValueSet))) { + + $this->properties[$shorthand] = new ValueSet($shorthand, $config); + } + + $this->properties[$shorthand]->add($declaration); + } else { + + $shorthand = Config::getPath('map.' . $name . '.shorthand'); + + // is is a shorthand property? + if (!is_null($shorthand)) { + + $config = Config::getPath('map.' . $shorthand); + + if (!isset($this->properties[$shorthand]) || !($this->properties[$shorthand] instanceof ValueMap)) { + + $this->properties[$shorthand] = new ValueMap($shorthand, $config); + } + + $this->properties[$shorthand]->add($declaration); + } else { + + // regular property + if (!isset($this->properties[$propertyName])) { + + $this->properties[$propertyName] = $declaration; + } + } + } + + return $this; + } + + /** + * set property + * @param string|null $name + * @param object[]|string $value + * @param string|null $propertyType + * @param array|null $leadingcomments + * @param array|null $trailingcomments + * @param string|null $src + * @param string|null $vendor + * @return $this + */ + + public function set(?string $name, $value, ?string $propertyType = null, ?array $leadingcomments = null, ?array $trailingcomments = null, ?string $src = null, ?string $vendor = null): ValueList + { + + $parseName = $name && $name[0] == '-' && ($name[1] ?? null) != '-' ? Lexer::parseVendor($name) : $name; + + $declaration = (object)[ + + 'name' => $parseName['name'] ?? $name, + 'type' => $propertyType ?? 'Declaration' + ]; + +// if (!is_null($src)) { +// +// $declaration->src = $src; +// } + + if (!is_null($value)) { + + $declaration->value = $value; + } + + if (!is_null($leadingcomments)) { + + $declaration->leadingcomments = $leadingcomments; + } + + if (!is_null($trailingcomments)) { + + $declaration->trailingcomments = $trailingcomments; + } + + if (is_null($vendor) && isset($parseName['vendor'])) { + + $vendor = $parseName['vendor']; + } + + if (!is_null($vendor)) { + + $declaration->vendor = $vendor; + } + + return $this->add($declaration); + } + + /** + * convert properties to string + * @param string $glue + * @param string $join + * @return string + * @throws Exception + */ + public function render(string $glue = ';', string $join = "\n"): string + { + + $result = []; + $renderer = new Renderer($this->options); + + foreach ($this->getIterator() as $property) { + + $output = $renderer->renderAst($property); + + if ($property->type != 'Comment') { + + $output .= $glue; + } + + $output .= $join; + $result[] = $output; + } + + return rtrim(rtrim(implode('', $result)), $glue); + } + + /** + * convert this object to string + * @return string + * @throws Exception + */ + public function __toString() + { + + return $this->render(); + } + + /** + * return properties iterator + * @return \Generator + * @throws Exception + */ + public function getIterator(): \Generator + { + + /** + * @var stdClass[] $result + */ +// $result = []; + + foreach ($this->properties as $property) { + + if (is_callable([$property, 'getIterator'])) { + + yield from $property->getIterator(); + } else { + + yield $property; + } + } + +// return new ArrayIterator($result); + } + + /** + * @return bool + */ + public function isEmpty(): bool + { + + return empty($this->properties); + } +} diff --git a/src/Element/Declaration/ValueMap.php b/src/Element/Declaration/ValueMap.php new file mode 100755 index 00000000..19581b2f --- /dev/null +++ b/src/Element/Declaration/ValueMap.php @@ -0,0 +1,349 @@ +shorthand = $shorthand; + + $config['required'] = []; + + if (isset($config['properties'])) { + + foreach ($config['properties'] as $property) { + + $config[$property] = Config::getPath('map.' . $property); + + unset($config[$property]['shorthand']); + + $this->property_type[$property] = $config[$property]; + + if (empty($config[$property]['optional'])) { + $config['required'][] = $property; + } + } + } + + $this->config = $config; + } + + public function add($declaration): static + { + + if (is_scalar($declaration->value)) { + + $declaration->value = Value::parse($declaration->value, $declaration->name); + } + + $name = $declaration->name; + $value = $declaration->value; + +// $propertyName = ($vendor ? '-' . $vendor . '-' : '') . $name; + $propertyName = $name; + + // is valid property + if (($this->shorthand != $propertyName) && !in_array($propertyName, $this->config['properties'])) { + + throw new InvalidArgumentException('Invalid property ' . $name, 400); + } + + // the type matches the shorthand - example system font + if ($propertyName == $this->shorthand || !isset($this->properties[$this->shorthand])) { + + if ($name == $this->shorthand) { + + $this->properties = []; + } + + $this->properties[$propertyName] = $declaration; + return $this; + } + + $this->properties[$propertyName] = $declaration; + $separator = Config::getPath('map.' . $this->shorthand . '.separator'); + + $all = []; + + if (is_null($separator)) { + + $all = [$this->properties[$this->shorthand]->value]; + } else { + + if (!is_array($value)) { + + $value = Value::parse($value, $name, preserve_quotes: true); + } + + $index = 0; + foreach ($this->properties[$this->shorthand]->value as $v) { + + if ($v->type == 'separator' && $v->value == $separator) { + + $index++; + continue; + } + + $all[$index][] = $v; + } + + $index = 0; + foreach ($value as $v) { + + if ($v->type == 'separator' && $v->value == $separator) { + + $index++; + continue; + } + + $all[$index][] = $v; + } + } + + $props = []; + foreach ($this->properties as $key => $prop) { + + if ($key == $this->shorthand) { + + continue; + } + + $sep = Config::getPath('properties.' . $key . '.separator'); + $v = []; + + if (is_null($sep)) { + + $v = [$prop->value]; + } else { + + $index = 0; + + foreach ($prop->value as $val) { + + if ($val->type == 'separator' && $val->value == $separator) { + + $index++; + continue; + } + + $v[$index][] = $val; + } + } +; + if (count($v) != count($all)) { + + return $this; + } + + $props[$key] = $v; + } + + $properties = $this->property_type; + $results = []; + + foreach ($all as $index => $values) { + + $data = []; + + foreach ($values as $val) { + + if (in_array($val->type, ['separator', 'whitespace'])) { + + continue; + } + + if (!isset($data[$val->type])) { + + $data[$val->type] = $val; + } else { + + if (!is_array($data[$val->type])) { + + $data[$val->type] = [$data[$val->type]]; + } + + $data[$val->type][] = $val; + } + } + + foreach ($props as $k => $prop) { + + if ($name == $this->shorthand) { + + continue; + } + + $data[$k] = $prop[$index]; + } + + // match + $patterns = $this->config['pattern']; + + foreach ($patterns as $p => $pattern) { + + foreach (preg_split('#(\s+)#', $pattern, -1, PREG_SPLIT_NO_EMPTY) as $token) { + + if (empty($this->property_type[$token]['optional']) && (!isset($data[$token]) || (is_array($data[$token]) && !isset($data[$token][$index])))) { + + unset($patterns[$p]); + } + } + } + + if (empty($patterns)) { + + return $this; + } + + // + foreach ($data as $key => $val) { + + if (!is_array($val)) { + + $val = [$val]; + } + + $set = []; + + if (isset($properties[$key]['prefix'])) { + + $prefix = $properties[$key]['prefix']; + $set[] = (object)['type' => 'separator', 'value' => is_array($prefix) ? $prefix[1] : $prefix]; + } + + $set[] = $val[0]; + + // + if (Config::getPath('map.' . $key . '.multiple')) { + + $i = 0; + $j = count($val); + $sp = Config::getPath('map.' . $key . '.separator', ' '); + + $sp = $sp == ' ' ? ['type' => 'whitespace'] : ['type' => 'separator', 'value' => $sp]; + + while (++$i < $j) { + + $set[] = clone((object)$sp); + $set[] = $val[$i]; + } + } + + $data[$key] = $set; + } + + $set = []; + + foreach (preg_split('#(\s+)#', $pattern, -1, PREG_SPLIT_DELIM_CAPTURE) as $token) { + + if (isset($data[$token]) && isset($properties[$token]['prefix']) && is_array($properties[$token]['prefix'])) { + + $res = $set; + $j = count($res); + + while ($j--) { + + if (in_array($res[$j]->type, ['whitespace', 'separator'])) { + + continue; + } + + if ((isset($properties[$token]['multiple']) && $res[$j]->type == $token) || + $res[$j]->type == $properties[$token]['prefix'][0]['type']) { + + break; + } + + if ($res[$j]->type != $properties[$token]['prefix'][0]['type']) { + + return $this; + } + } + } + + if (trim($token) == '') { + + $set[] = (object)['type' => 'whitespace']; + } else if (isset($data[$token])) { + + array_splice($set, count($set), 0, $data[$token]); + } + } + + $results[] = $set; + } + + $set = []; + + $i = 0; + $j = count($results); + + array_splice($set, count($set), 0, $results[0]); + + while (++$i < $j) { + $set[] = (object)['type' => 'separator', 'value' => $separator]; + + array_splice($set, count($set), 0, $results[$i]); + } + + $data = Value::reduce($set, ['remove_defaults' => true]); + + if (empty($data)) { + + $this->properties[$name] = (object)array_merge(['type' => 'Declaration', 'name' => $name, 'value' => $value], Lexer::parseVendor($propertyName)); + return $this; + } + + $this->properties = [$this->shorthand => (object)array_merge(['type' => 'Declaration', 'name' => $this->shorthand, 'value' => $data], Lexer::parseVendor($this->shorthand))]; + return $this; + } + + /** + * return Property array + */ + public function getIterator(): \Generator + { + + yield from $this->properties; + } +} \ No newline at end of file diff --git a/src/Element/Declaration/ValueSet.php b/src/Element/Declaration/ValueSet.php new file mode 100755 index 00000000..fe15ba20 --- /dev/null +++ b/src/Element/Declaration/ValueSet.php @@ -0,0 +1,483 @@ +shorthand = $shorthand; + + foreach ($config['properties'] as $property) { + + $config[$property] = Config::getProperty($property); + $this->property_type[$config[$property]['type']][] = $property; + + unset($config[$property]['shorthand']); + } + + $this->config = $config; + + if (isset($config['pattern']) && is_array($config['pattern'])) { + + $this->config['pattern'] = $config['pattern'][0]; + } + } + + public function add($declaration): static + { + + if (is_scalar($declaration->value)) { + + $declaration->value = Value::parse($declaration->value, $declaration->name); + } + + $name = $declaration->name ?? null; + $vendor = $declaration->vendor ?? null; + + $propertyName = ($vendor ? '-' . $vendor . '-' : '') . $name; + + // is valid property + if (($this->shorthand != $propertyName) && !in_array($propertyName, $this->config['properties'])) { + + throw new InvalidArgumentException('Invalid property ' . $propertyName . ' : ' . $this->shorthand, 400); + } + + if (!$this->isImportant) { + +// $values = $declaration->value; + $i = count($declaration->value); + + while ($i--) { + + $token = $declaration->value[$i]; + + if ($token->type == 'Comment') { + + continue; + } + + if ($token->type == 'css-string' && $token->value == '!important') { + + $this->isImportant = true; + } + + break; + } + } + + if ($this->isImportant) { + + $this->properties[$propertyName] = $declaration; + return $this; + } + + // $name is shorthand -> expand + if ($this->shorthand == $propertyName) { + + foreach ($this->config['properties'] as $property) { + + unset($this->properties[$property]); + } + + $result = $this->expand($declaration->value); + + if ($result === false) { + + $this->properties[$propertyName] = $declaration; + return $this; + } + + if (is_array($result)) { + + $this->expandProperties($result); + unset($this->properties[$this->shorthand]); + } else { + + $this->properties[$propertyName] = $declaration; + } + + } else { + + if (isset($this->properties[$this->shorthand])) { + + $shorthandValue = $this->properties[$this->shorthand]->value; + + $result = $this->expand($shorthandValue); + + if ($result !== false) { + + $this->expandProperties($result); + } else { + + foreach ($this->config['properties'] as $property) { + + if (isset($this->properties[$property])) { + + $this->properties[$property]->value = $shorthandValue; + } else { + + $this->properties[$property] = (object)array_merge(['type' => 'Declaration', 'name' => $propertyName, 'value' => $shorthandValue], Lexer::parseVendor($propertyName)); + } + } + } + + unset($this->properties[$this->shorthand]); + } + + $this->properties[$propertyName] = $declaration; + } + + return $this; + } + + /** + * @param array $result + * @return $this + */ + protected function expandProperties(array $result): static + { + + foreach ($result as $property => $values) { + + if (isset($this->properties[$property])) { + + $this->properties[$property]->value = $values; + } else { + + $this->properties[$property] = (object)array_merge(['type' => 'Declaration', 'name' => $property, 'value' => $values], Lexer::parseVendor($property)); + } + + +// $this->setProperty($property, $values, $vendor); +// +// if (!is_null($leadingcomments)) { +// +// $this->properties[$property]->setLeadingComments($leadingcomments); +// } +// +// if (!is_null($trailingcomments)) { +// +// $this->properties[$property]->setTrailingComments($trailingcomments); +// } + } + + return $this; + } + + /** + * expand shorthand property + * @param array|string $value + * @return array|bool + * @ignore + */ + protected function expand($value) + { + + if (is_string($value)) { + + $value = Value::parse($value, $this->shorthand, true, '', '', true); + } + + $pattern = explode(' ', $this->config['pattern']); + $value_map = []; + $values = []; + $result = []; + + $separator = $this->config['separator'] ?? null; + $index = 0; + + foreach ($value as $v) { + + if ($v->type == 'css-string' && $v->value == '!important') { + + return false; + } + + if (isset($v->value) && $v->value == $separator) { + + $index++; + } else { + + $value_map[$index][] = $v; + } + } + + $vmap = $value_map; + + foreach ($pattern as $match) { + + foreach ($value_map as $index => $map) { + + foreach ($map as $i => $v) { + + $className = Value::getClassName($v->type); + + if ($v->type == 'whitespace') { + + unset($vmap[$index][$i]); + } else if ($className::match($v, $match)) { + + $values[$index][$match][] = $v; + unset($vmap[$index][$i]); + break; + } + } + + if (empty($vmap[$index])) { + + unset($value_map[$index]); + } else { + + $value_map[$index] = $vmap[$index]; + } + } + } + + // does not match the pattern + if (!empty($value_map)) { + + return false; + } + + foreach ($values as $types) { + + foreach ($this->property_type as $unit => $properties) { + + foreach ($properties as $property) { + + // value not set + if (!isset($types[$unit])) { + + continue; + } + + $index = array_search($property, $this->property_type[$unit], true); + $key = null; + + $list = $types[$unit]; + + if (isset($list[$index])) { + + $key = $index; + } else { + + if (isset($this->config['value_map'][$property])) { + + foreach ($this->config['value_map'][$property] as $item) { + + if (isset($list[$item])) { + + $key = $item; + break; + } + } + } + } + + if (!is_null($key)) { + + $result[$property][] = $list[$key]; + } + } + } + } + + foreach ($result as $property => $values) { + + $i = count($values); + + if ($i > 0) { + + $separator = $this->config[$property]['separator'] ?? ' '; + + $separator = (object)($separator == ' ' ? [ + + 'type' => 'whitespace' + ] : + [ + 'type' => 'separator', + 'value' => $separator + ]); + + while ($i-- > 1) { + + array_splice($result[$property], $i, 0, [clone $separator]); + } + } + } + + return $result; + } + + /** + * return Property array + * @throws \Exception + */ + public function getIterator(): \Generator + { + + $properties = $this->properties; + + foreach ($properties as $property) { + + foreach ($property->value as $value) { + + if ($value->type == 'css-string' && $value->value == '!important') { + + yield from $properties; + return; + } + } + } + + if (isset($this->config['value_map']) && count($this->config['properties']) == count($properties)) { + + $all = []; + $keys = array_keys($properties); + + foreach ($keys as $property) { + + $all[$property] = Value::splitValues($properties[$property]->value, $property); + } + + $count = count(current($all)); + + foreach ($all as $item) { + + if (count($item) != $count) { + + yield from $properties; + return; + } + } + + $result = []; + $separator = Config::getProperty($this->config['shorthand'] . '.separator', ' '); + + for ($i = 0; $i < $count; $i++) { + + $values = []; + + foreach ($keys as $key) { + + $values[$key] = $all[$key][$i]; + } + + foreach ($this->config['value_map'] as $property => $set) { + + $prop = $this->config['properties'][$set[0]]; + + if (!isset($properties[$property]) || !isset($properties[$prop])) { + + break; + } + + $v1 = $values[$property]; + $v2 = $values[$prop]; + + if (count($v1) == count($v2) && Value::equals($v1, $v2)) { + + unset($values[$property]); + continue; + } + + break; + } + + $result[$i] = $values; + } + + $token = (object)['type' => $separator == ' ' ? 'whitespace' : 'separator']; + + if ($token->type == 'separator') { + + $token->value = $separator; + } + + $values = []; + + $v = []; + foreach ($result[0] as $val) { + + array_splice($v, count($v), 0, $val); + } + + $l = count($v); + + while ($l-- > 1) { + + array_splice($v, $l, 0, [(object)['type' => 'whitespace']]); + } + + array_splice($values, count($values), 0, $v); + + $j = count($result); + + for ($i = 1; $i < $j; $i++) { + + $values[] = clone $token; + + $v = []; + foreach ($result[$i] as $val) { + + array_splice($v, count($v), 0, $val); + } + + $l = count($v); + + while ($l-- > 1) { + + array_splice($v, $l, 0, [(object)['type' => 'whitespace']]); + } + + array_splice($values, count($values), 0, $v); + } + + yield (object) array_merge(['type' => 'Declaration', 'name' => $this->shorthand, 'value' => $values ], Lexer::parseVendor($this->shorthand)); + return; + } + + yield from $properties; + } +} \ No newline at end of file diff --git a/src/Element/ElementTrait.php b/src/Element/ElementTrait.php deleted file mode 100755 index c1dda3e5..00000000 --- a/src/Element/ElementTrait.php +++ /dev/null @@ -1,87 +0,0 @@ -getVendor(); - - if ($getVendor && $vendor !== '') { - - return '-'.$vendor.'-'.$this->ast->name; - } - - return $this->ast->name; - } - - /** - * get node name - * @param string $name - * @return \TBela\CSS\Element - */ - public function setName (string $name) { - - $name = trim($name); - if (preg_match('/^(-([a-zA-Z]+)-(\S+))/', $name, $match)) { - - $this->ast->vendor = $match[2]; - $this->ast->name = Value::escape($match[3]); - } - - else { - - $this->ast->name = Value::escape($name); - } - - return $this; - } - - /** - * @param string|null $prefix - * @return $this - */ - public function setVendor ($prefix) { - - if (is_null($prefix) || (string) $prefix === '') { - - echo (new Exception())."\n\n"; - unset($this->ast->vendor); - } - - else { - - $this->ast->vendor = $prefix; - } - - return $this; - } - - /** - * set vendor prefix - * @return string - */ - public function getVendor() : string { - - if (isset($this->ast->vendor)) { - - return $this->ast->vendor; - } - - return ''; - } -} \ No newline at end of file diff --git a/src/Element/NestingRule.php b/src/Element/NestingRule.php index c9995c77..5e29b7e0 100755 --- a/src/Element/NestingRule.php +++ b/src/Element/NestingRule.php @@ -10,7 +10,7 @@ class NestingRule extends Rule /** * @inheritDoc */ - public function support(ElementInterface $child) + public function support(ElementInterface $child): bool { return true; diff --git a/src/Element/Rule.php b/src/Element/Rule.php index 11565159..413c4be4 100755 --- a/src/Element/Rule.php +++ b/src/Element/Rule.php @@ -3,158 +3,164 @@ namespace TBela\CSS\Element; use Exception; -use \TBela\CSS\Interfaces\ElementInterface; +use TBela\CSS\Interfaces\ElementInterface; +use TBela\CSS\Interfaces\RuleInterface; use TBela\CSS\Value; -class Rule extends RuleList +class Rule extends RuleList implements RuleInterface { - /** - * Return the css selectors - * @return string[] - */ - public function getSelector() - { - return $this->ast->selector; - } + /** + * Return the css selectors + * @return string[] + */ + public function getSelector(): array + { - protected function parseSelector($selectors) - { + return $this->ast->selector; + } - if (is_array($selectors) && is_string($selectors[0])) { + /** + * @throws Exception + */ + protected function parseSelector($selectors): array + { - $selectors = implode(',', $selectors); - } + if (is_array($selectors) && is_string($selectors[0])) { - if (is_string($selectors)) { + $selectors = implode(',', $selectors); + } - $selectors = Value::parse($selectors, null, true, '', ''); - } + if (is_string($selectors)) { - $selectors = Value::split(Value::renderTokens($selectors, ['omit_unit' => false]), ','); + $selectors = Value::parse($selectors); + } - $result = []; + $selectors = Value::split(Value::renderTokens($selectors, ['omit_unit' => false]), ','); + $result = []; - foreach ($selectors as $selector) { + foreach ($selectors as $selector) { - $selector = trim($selector); - $result[$selector] = $selector; - } + $selector = trim($selector); + $result[$selector] = $selector; + } - return array_values($result); - } + return array_values($result); + } - /** - * Set css rule selector - * @param string|array $selectors - * @return $this - */ - public function setSelector($selectors): static + /** + * Set css rule selector + * @param array|string $selector + * @return $this + * @throws Exception + */ + public function setSelector(array|string $selector): static { - $this->ast->selector = $this->parseSelector($selectors); - return $this; - } - - /** - * Add css selectors - * @param array|string $selector - * @return $this - */ - public function addSelector($selector): static + $this->ast->selector = $this->parseSelector($selector); + return $this; + } + + /** + * Add css selectors + * @param array|string $selector + * @return $this + * @throws Exception + */ + public function addSelector(array|string $selector): static { - $result = []; + $result = []; - foreach ($this->ast->selector as $r) { + foreach ($this->ast->selector as $r) { - $result[trim($r)] = $r; - } + $result[trim($r)] = $r; + } - foreach ($this->parseSelector($selector) as $r) { + foreach ($this->parseSelector($selector) as $r) { - $result[trim($r)] = $r; - } + $result[trim($r)] = $r; + } - $this->ast->selector = array_values($result); - return $this; - } + $this->ast->selector = array_values($result); + return $this; + } - /** - * Remove a css selector - * @param array|string $selector - * @return Rule - * @throws Exception - */ - public function removeSelector($selector): static + /** + * Remove a css selector + * @param array|string $selector + * @return Rule + * @throws Exception + */ + public function removeSelector(array|string $selector): static { - if (!is_array($selector)) { + if (!is_array($selector)) { - $selector = array_map('trim', explode(',', $selector)); - } + $selector = array_map('trim', explode(',', $selector)); + } - $selector = array_values(array_diff($this->ast->selector, $selector)); + $selector = array_values(array_diff($this->ast->selector, $selector)); - if (empty($selector)) { + if (empty($selector)) { - throw new \Exception(sprintf('the selector is empty: %s:%s:%s', $this->ast->src ?? '', $this->ast->position->line ?? '', $this->ast->position->column ?? ''), 400); - } + throw new Exception(sprintf('the selector is empty: %s:%s:%s', $this->ast->src ?? '', $this->ast->position->line ?? '', $this->ast->position->column ?? ''), 400); + } - $this->ast->selector = $selector; + $this->ast->selector = $selector; - return $this; - } + return $this; + } - /** - * Add css declaration - * @param string $name - * @param string $value - * @return Declaration - * @throws Exception - */ - public function addDeclaration(string $name, string $value): Declaration + /** + * Add css declaration + * @param string $name + * @param string $value + * @return Declaration + * @throws Exception + */ + public function addDeclaration(string $name, string $value): Declaration { - $declaration = new Declaration(); + $declaration = new Declaration(); - $declaration['name'] = $name; - $declaration['value'] = $value; + $declaration['name'] = $name; + $declaration['value'] = $value; - return $this->append($declaration); - } + return $this->append($declaration); + } - /** - * Merge another css rule into this - * @param Rule $rule - * @return Rule $this - * @throws Exception - */ - public function merge(Rule $rule): static + /** + * Merge another css rule into this + * @param Rule $rule + * @return Rule $this + * @throws Exception + */ + public function merge(Rule $rule): static { - $this->addSelector($rule->getSelector()); + $this->addSelector($rule->getSelector()); - foreach ($rule->getChildren() as $element) { + foreach ($rule->getChildren() as $element) { - $this->addDeclaration($element->getName(), $element->getValue()); - } + $this->addDeclaration($element->getName(), $element->getValue()); + } - return $this; - } + return $this; + } - /** - * @inheritDoc - */ - public function support(ElementInterface $child): bool + /** + * @inheritDoc + */ + public function support(ElementInterface $child): bool { - if ($child instanceof Comment) { + if ($child instanceof Comment) { - return true; - } + return true; + } - return $child instanceof Declaration; - } + return $child instanceof Declaration; + } } \ No newline at end of file diff --git a/src/Element/RuleList.php b/src/Element/RuleList.php index 95931330..addb0fcb 100755 --- a/src/Element/RuleList.php +++ b/src/Element/RuleList.php @@ -5,12 +5,12 @@ use ArrayIterator; use InvalidArgumentException; use TBela\CSS\Ast\NodeWalker; +use TBela\CSS\Declaration\Property; use TBela\CSS\Element; +use TBela\CSS\Element\Declaration\ValueList; use TBela\CSS\Interfaces\ElementInterface; use TBela\CSS\Interfaces\RuleListInterface; use TBela\CSS\Parser; -use TBela\CSS\Property\Property; -use TBela\CSS\Property\PropertyList; use Traversable; use function in_array; @@ -81,7 +81,7 @@ public function computeShortHand(): RuleListInterface { if ($nodeType == 'Rule' || ($nodeType == 'AtRule' && $node->hasDeclarations())) { - $propertyList = new PropertyList($node, ['compute_shorthand' => true]); + $propertyList = new ValueList($node, ['compute_shorthand' => true]); $node->removeChildren(); diff --git a/src/Interfaces/AtRuleInterface.php b/src/Interfaces/AtRuleInterface.php new file mode 100644 index 00000000..6fefefdb --- /dev/null +++ b/src/Interfaces/AtRuleInterface.php @@ -0,0 +1,9 @@ + ['font-face']]); + public function deduplicate(array $options = ['allow_duplicate_rules' => ['font-face']]): ElementInterface; /** * convert to string * @return string */ - public function __toString(); + public function __toString(): string; /** * clone object diff --git a/src/Interfaces/NamedElementInterface.php b/src/Interfaces/NamedElementInterface.php new file mode 100644 index 00000000..2c23fee7 --- /dev/null +++ b/src/Interfaces/NamedElementInterface.php @@ -0,0 +1,18 @@ + true, 'multi_processing' => true, // buffer size 32k. higher values may break the execution - 'multi_processing_threshold' => 64 * 1024, + 'multi_processing_threshold' => 30 * 1024, // 'children_process' => 20, 'ast_src' => '', 'ast_position_line' => 1, @@ -241,6 +241,9 @@ public function parallelize(string $content, object $root, string $file): void then(function (array $result, int $index) use (&$data) { $data[$index] = $result; + })->catch(function (\Throwable $e) use($file, $slice) { + + throw new \RuntimeException(sprintf('error parsing %s:%s:%s', $file, $slice[1]->line, $slice[1]->column), $e->getCode(), $e); }); } @@ -773,9 +776,9 @@ protected function computeSignature(object $ast): string $signature .= ':vendor:' . $vendor; } - if (in_array($ast->type, ['AtRule', 'NestingAtRule', 'NestingMediaRule'])) { + if ($ast->type != 'Declaration' && !isset($selector)) { - $signature .= ':value:' . (is_array($ast->value) ? Value::renderTokens($ast->value) : $ast->value); + $signature .= ':value:' .(is_array($ast->value) ? Value::renderTokens($ast->value) : $ast->value); } return $signature; diff --git a/src/Parser/Lexer.php b/src/Parser/Lexer.php index 53ca1b63..c4507b5a 100755 --- a/src/Parser/Lexer.php +++ b/src/Parser/Lexer.php @@ -299,6 +299,7 @@ public function doTokenize(string $css, ?string $src, bool $recover, object $con $this->parseComments($declaration); $data = $declaration->value; + $declaration->tokens = is_array($data) ? $data : Value::parse($declaration->value, $declaration->name); if (is_array($data)) { @@ -374,6 +375,12 @@ public function doTokenize(string $css, ?string $src, bool $recover, object $con } } + if (is_array($declaration->value)) { + + $declaration->tokens = $declaration->value; + $declaration->value = Value::renderTokens($declaration->value); + } + yield 'enter' => [$declaration, $context, $parentStylesheet]; } } @@ -407,7 +414,29 @@ public function doTokenize(string $css, ?string $src, bool $recover, object $con if (str_contains($rule->value, '/*') || $rule->name == 'charset') { - $rule->value = Value::parse($rule->value, null, true, '', '', $rule->name == 'charset'); + $leadingcomments = true; + $rule->tokens = array_filter(Value::parse($rule->value, preserve_quotes: $rule->name == 'charset'), function ($token) use($rule, &$leadingcomments) { + + if ($token->type == 'Comment') { + + if ($leadingcomments) { + + $rule->leadingcomments[] = $token->value; + } + else { + + $rule->trailingcomments[] = $token->value; + } + + return false; + } + + $leadingcomments = false; + + return true; + }); + + $rule->value = $rule->tokens; // Value::renderTokens($rule->tokens); } if ($rule->hasDeclarations) { @@ -663,8 +692,7 @@ public function setParentOffset(object $parentOffset): static /** * * @return object - * @ignore - */ + * @ignore */ public function createContext(): object { @@ -759,7 +787,7 @@ protected function parseComments(object $token): void if (is_string($token->value)) { - $token->value = Value::parse($token->value, in_array($token->type, ['Declaration', 'Property']) ? $token->name : null, preserve_quotes: true); + $token->value = Value::parse($token->value, $token->type == 'Declaration' ? $token->name : null, preserve_quotes: true); } $trailing = []; @@ -798,7 +826,7 @@ protected function parseComments(object $token): void * @return array * @ignore */ - protected function parseVendor(string $str): array + public static function parseVendor(string $str): array { if (preg_match('/^(-([a-zA-Z]+)-(\S+))/', trim($str), $match)) { diff --git a/src/Parser/ParserTrait.php b/src/Parser/ParserTrait.php index 937c6f22..480947cb 100755 --- a/src/Parser/ParserTrait.php +++ b/src/Parser/ParserTrait.php @@ -2,7 +2,7 @@ namespace TBela\CSS\Parser; -use TBela\CSS\Property\Config; +use TBela\CSS\Config; use function substr; trait ParserTrait diff --git a/src/Parser/Validator/NestingAtRule.php b/src/Parser/Validator/NestingAtRule.php index 0589acc7..91e9d6d5 100644 --- a/src/Parser/Validator/NestingAtRule.php +++ b/src/Parser/Validator/NestingAtRule.php @@ -10,7 +10,10 @@ class NestingAtRule implements ValidatorInterface { use ValidatorTrait; - public function validate(object $token, object $parentRule, object $parentStylesheet): int + /** + * @throws \Exception + */ + public function validate(object $token, object $parentRule, object $parentStylesheet): int { $this->error = null; @@ -22,11 +25,11 @@ public function validate(object $token, object $parentRule, object $parentStyles return static::REJECT; } - $selectors = is_array($token->selector) ? Value::renderTokens($token->selector) : $token->selector; + $selectors = is_array($token->selector) ? $token->selector : Value::split($token->selector,','); - foreach (Value::split($selectors, ',') as $selector) { + foreach ($selectors as $selector) { - if (strpos($selector, '&') === false) { + if (!str_contains($selector, '&')) { $this->error = new SyntaxError('the selector must contain "&'); return static::REJECT; diff --git a/src/Property/Comment.php b/src/Property/Comment.php deleted file mode 100755 index 48c32111..00000000 --- a/src/Property/Comment.php +++ /dev/null @@ -1,101 +0,0 @@ -setValue($value); - } - - public function getName(bool $vendor = false) { - - return null; - } - - /** - * Set the value - * @param array | string $value - * @return $this - */ - public function setValue($value) { - - $this->value = $value; - return $this; - } - - /** - * Return the object value - * @return string - */ - public function getValue() { - - return $this->value; - } - - /** - * Converty this object to string - * @param array $options - * @return string - */ - public function render (array $options = []) { - - if (!empty($options['remove_comments'])) { - - return ''; - } - - return $this->value; - } - - /** - * @inheritDoc - */ - public function setTrailingComments(?array $comments): RenderableInterface - { - return $this; - } - - /** - * @inheritDoc - */ - public function getTrailingComments(): ?array - { - return null; - } - - /** - * @inheritDoc - */ - public function setLeadingComments(?array $comments): RenderableInterface - { - return $this; - } - - /** - * @inheritDoc - */ - public function getLeadingComments(): ?array - { - return null; - } -} \ No newline at end of file diff --git a/src/Property/Property.php b/src/Property/Property.php deleted file mode 100755 index c2052118..00000000 --- a/src/Property/Property.php +++ /dev/null @@ -1,209 +0,0 @@ -setName($name); - } - - /** - * set the property value - * @param array|string $value - * @return Property - */ - public function setValue($value) { - - $this->value = is_array($value) ? $value : Value::parse($value, $this->name, true, '', ''); - return $this; - } - - /** - * get the property value - * @return array|null - */ - public function getValue() { - - return $this->value; - } - - /** - * @param $vendor - * @return Property - */ - public function setVendor($vendor) { - - $this->vendor = $vendor; - return $this; - } - - /** - * @return string|null - */ - public function getVendor() { - - return $this->vendor ?? null; - } - - /** - * @param string $name - * @return Property - */ - public function setName($name) { - - if (substr($name, 0, 1) == '-' && preg_match('/^(-([a-zA-Z]+)-(\S+))/', trim($name), $match)) { - - $this->name = $match[3]; - $this->vendor = $match[2]; - } - - else { - - $this->name = (string) $name; - } - - return $this; - } - - /** - * get the property name - * @return string|null - */ - public function getName(bool $vendor = true) { - - return ($vendor && $this->vendor ? '-'.$this->vendor.'-' : '').$this->name; - } - - /** - * return the property type - * @return string - */ - public function getType() { - - return $this->type; - } - - /** - * convert property to string - * @param array $options - * @return string - */ - public function render (array $options = []) { - - $result = ($this->vendor ? '-'.$this->vendor.'-' : null).$this->name; - - if (!empty($this->leadingcomments)) { - - $result .= ' '.implode(' ', $this->leadingcomments); - } - - $result .= ': '.Value::renderTokens($this->value, $options); - - if (!empty($this->trailingcomments)) { - - $result .= ' '.implode(' ', $this->trailingcomments); - } - - return $result; - } - - /** - * convert this object to string - * @return string - */ - public function __toString () { - - return $this->render(); - } - - /** - * @inheritDoc - */ - public function setTrailingComments(?array $comments):RenderableInterface - { - $this->trailingcomments = $comments; - return $this; - } - - /** - * @inheritDoc - */ - public function getTrailingComments(): ?array - { - return $this->trailingcomments; - } - - /** - * @inheritDoc - */ - public function setLeadingComments(?array $comments):RenderableInterface - { - $this->leadingcomments = $comments; - return $this; - } - - /** - * @inheritDoc - */ - public function getLeadingComments(): ?array - { - return $this->leadingcomments; - } - - /** - * @inheritDoc - */ - public function getAst() - { - return (object) array_filter(get_object_vars($this), function ($value) { - - return !is_null($value); - }); - } -} \ No newline at end of file diff --git a/src/Property/PropertyList.php b/src/Property/PropertyList.php deleted file mode 100755 index d9fde4bf..00000000 --- a/src/Property/PropertyList.php +++ /dev/null @@ -1,383 +0,0 @@ - true, - 'allow_duplicate_declarations' => false - ]; - - /*** - * PropertyList constructor. - * @param RuleList|null $list - * @param array $options - */ - public function __construct(RuleList $list = null, array $options = []) - { - - $this->options = array_merge($this->options, $options); - - if ((is_callable([$list, 'hasDeclarations']) && $list->hasDeclarations()) || $list instanceof Rule) { - - foreach ($list as $element) { - - $this->set($element['name'], $element['value'], $element['type'], $element['leadingcomments'], $element['trailingcomments']); - } - } - } - - // @todo vendor prefix support - public function has($property) { - - if (isset($this->properties[$property])) { - - return true; - } - - $shorthand = Config::getProperty($property.'.shorthand'); - - if (!isset($shorthand)) { - - $shorthand = Config::getPath('map.'.$property.'.shorthand'); - } - - if (isset($shorthand) && isset($this->properties[$shorthand])) { - - - return $this->properties[$shorthand]->has($property); - } - - return false; - } - - // @todo vendor prefix support - public function remove($property): static - { - if (isset($this->properties[$property])) { - - unset($this->properties[$property]); - } - - else { - - $shorthand = Config::getProperty($property.'.shorthand'); - - if (!isset($shorthand)) { - - $shorthand = Config::getPath('map.'.$property.'.shorthand'); - } - - if (isset($shorthand) && isset($this->properties[$shorthand])) { - - $this->properties[$shorthand]->remove($property); - - if ($this->properties[$shorthand]->isEmpty()) { - - unset($this->properties[$shorthand]); - } - } - } - - return $this; - } - - /** - * set property - * @param string|null $name - * @param object[]|string $value - * @param string|null $propertyType - * @param array|null $leadingcomments - * @param array|null $trailingcomments - * @param string|null $src - * @param string|null $vendor - * @return $this - */ - - public function set(?string $name, $value, ?string $propertyType = null, ?array $leadingcomments = null, ?array $trailingcomments = null, ?string $src = null, ?string $vendor = null): PropertyList - { - - if ($propertyType == 'Comment') { - - $this->properties[] = new Comment($value); - return $this; - } - - $name = (string) $name; - - if(!empty($this->options['allow_duplicate_declarations'])) { - - if ($this->options['allow_duplicate_declarations'] === true || - (is_array($this->options['allow_duplicate_declarations']) && in_array($name, $this->options['allow_duplicate_declarations']))) { - - $property = (new Property($name))->setValue($value); - - if (!is_null($src)) { - - $property->setSrc($src); - } - - if (!is_null($vendor)) { - - $property->setVendor($vendor); - } - - if (!empty($leadingcomments)) { - - $property->setLeadingComments($leadingcomments); - } - - if (!empty($trailingcomments)) { - - $property->setTrailingComments($trailingcomments); - } - - $this->properties[] = $property; - return $this; - } - } - - $propertyName = $name; - - if (str_starts_with($name, '-') && preg_match('/^(-([a-zA-Z]+)-(\S+))/', trim($name), $match)) { - - $name = $match[3]; - - if (is_null($vendor)) { - - $vendor = $match[2]; - } - } - - if (!is_null($vendor)) { - - $propertyName = '-'.$vendor.'-'.$name; - } - - unset($this->properties[$propertyName]); - - if (empty($this->options['compute_shorthand'])) { - - $property = (new Property($name))->setValue($value); - - if (!is_null($src)) { - - $property->setSrc($src); - } - - if (!is_null($vendor)) { - - $property->setVendor($vendor); - } - - if (!empty($leadingcomments)) { - - $property->setLeadingComments($leadingcomments); - } - - if (!empty($trailingcomments)) { - - $property->setTrailingComments($trailingcomments); - } - - $name = $property->getName(true); - - $this->properties[$name] = $property; - return $this; - } - - $shorthand = Config::getProperty($propertyName.'.shorthand'); - - // is is a shorthand property? - if (!is_null($shorthand) && !is_null(Config::getProperty($shorthand))) { - - $config = Config::getProperty($shorthand); - - if (!isset($this->properties[$shorthand]) || (!($this->properties[$shorthand] instanceof PropertySet))) { - - $this->properties[$shorthand] = new PropertySet($shorthand, $config); - -// if (!is_null($src)) { -// -// $this->properties[$shorthand]->setSrc($src); -// } - } - - $this->properties[$shorthand]->set($name, $value, $leadingcomments, $trailingcomments, $vendor, $src); - } - - else { - - $shorthand = Config::getPath('map.'.$name.'.shorthand'); - - // is is a shorthand property? - if (!is_null($shorthand)) { - - $config = Config::getPath('map.'.$shorthand); - - if (!isset($this->properties[$shorthand])) { - - $this->properties[$shorthand] = new PropertyMap($shorthand, $config); - -// if (!is_null($src)) { -// -// $this->properties[$shorthand]->setSrc($src); -// } - } - - $this->properties[$shorthand]->set($name, $value, $leadingcomments, $trailingcomments, $src); - } - - else { - - // regular property - if (!isset($this->properties[$propertyName])) { - - $this->properties[$propertyName] = new Property($name); - } - - $property = $this->properties[$propertyName]->setValue($value); - - if (!is_null($vendor)) { - - $property->setVendor($vendor); - } - - if (!is_null($src)) { - - $property->setSrc($src); - } - - if (!empty($leadingcomments)) { - - $property->setLeadingComments($leadingcomments); - } - - if (!empty($trailingcomments)) { - - $property->setTrailingComments($trailingcomments); - } - } - } - - return $this; - } - - /** - * convert properties to string - * @param string $glue - * @param string $join - * @return string - */ - public function render ($glue = ';', $join = "\n") - { - - $result = []; - - foreach ($this->getProperties() as $property) { - - $output = $property->render($this->options); - - if (!($property instanceof Comment)) { - - $output .= $glue; - } - - $output .= $join; - $result[] = $output; - } - - return rtrim(rtrim(implode('', $result)), $glue); - } - - /** - * convert this object to string - * @return string - */ - public function __toString() { - - return $this->render(); - } - - /** - * return properties iterator - * @return ArrayIterator - */ - public function getProperties () { - - /** - * @var Property[] $result - */ - $result = []; - - foreach ($this->properties as $property) { - - if (is_callable([$property, 'getProperties'])) { - - array_splice($result, count($result), 0, $property->getProperties()); - } - - else { - - $result[] = $property; - } - } - - return new ArrayIterator($result); - } - - /** - * @return bool - */ - public function isEmpty() { - - return empty($this->properties); - } - - /** - * @inheritDoc - */ - public function getIterator() - { - return $this->getProperties(); - } - - /** - * @return array - * @ignore - */ - public function toObject() { - - $data = []; - - foreach ($this->getProperties() as $property) { - - $data[] = $property->toObject(); - } - - return $data; - } -} diff --git a/src/Property/PropertyMap.php b/src/Property/PropertyMap.php deleted file mode 100755 index 44133808..00000000 --- a/src/Property/PropertyMap.php +++ /dev/null @@ -1,446 +0,0 @@ -shorthand = $shorthand; - - $config['required'] = []; - - if (isset($config['properties'])) { - - foreach ($config['properties'] as $property) { - - $config[$property] = Config::getPath('map.' . $property); - - unset($config[$property]['shorthand']); - - $this->property_type[$property] = $config[$property]; - - if (empty($config[$property]['optional'])) { - $config['required'][] = $property; - } - } - } - - $this->config = $config; - } - - // @todo vendor prefix support - public function has($property): bool - { - - return isset($this->properties[$property]); - } - - // @todo vendor prefix support - public function remove($property): static - { - - unset($this->properties[$property]); - - return $this; - } - - /** - * set property value - * @param string $name - * @param object[]|string $value - * @param array|null $leadingcomments - * @param array|null $trailingcomments - * @return PropertyMap - */ - public function set(string $name, $value, ?array $leadingcomments = null, ?array $trailingcomments = null, $src = null) - { - - // is valid property - if (($this->shorthand != $name) && !in_array($name, $this->config['properties'])) { - - throw new InvalidArgumentException('Invalid property ' . $name, 400); - } - - // the type matches the shorthand - example system font - if ($name == $this->shorthand || !isset($this->properties[$this->shorthand])) { - - if ($name == $this->shorthand) { - - $this->properties = []; - } - - if (!isset($this->properties[$name])) { - - $this->properties[$name] = new Property($name); - } - - if ($src !== null) { - - $this->properties[$name]->setSrc($src); - } - - $this->properties[$name]->setValue($value)-> - setLeadingComments($leadingcomments)-> - setTrailingComments($trailingcomments); - - return $this; - } - - $this->properties[$name] = (new Property($name))->setValue($value)-> - setLeadingComments($leadingcomments)-> - setTrailingComments($trailingcomments); - - if ($src !== null) { - - $this->properties[$name]->setSrc($src); - } - - $separator = Config::getPath('map.' . $this->shorthand . '.separator'); - - $all = []; - - if (is_null($separator)) { - - $all = [$this->properties[$this->shorthand]->getValue()]; - } else { - - if (!is_array($value)) { - - $value = Value::parse($value, $name, true, '', '', true); - } - - $index = 0; - foreach ($this->properties[$this->shorthand]->getValue() as $v) { - - if ($v->type == 'separator' && $v->value == $separator) { - - $index++; - continue; - } - - $all[$index][] = $v; - } - - $index = 0; - foreach ($value as $v) { - - if ($v->type == 'separator' && $v->value == $separator) { - - $index++; - continue; - } - - $all[$index][] = $v; - } - } - - $props = []; - foreach ($this->properties as $key => $prop) { - - if ($key == $this->shorthand) { - - continue; - } - - $sep = Config::getPath('properties.' . $key . '.separator'); - $v = []; - - if (is_null($sep)) { - - $v = [$prop->getValue()]; - } else { - - $index = 0; - - foreach ($prop->getValue() as $val) { - - if ($val->type == 'separator' && $val->value == $separator) { - - $index++; - continue; - } - - $v[$index][] = $val; - } - } - - - if (count($v) != count($all)) { - - return $this; - } - - $props[$key] = $v; - } - - $properties = $this->property_type; - $results = []; - - foreach ($all as $index => $values) { - - $data = []; - - foreach ($values as $val) { - - if (in_array($val->type, ['separator', 'whitespace'])) { - - continue; - } - - if (!isset($data[$val->type])) { - - $data[$val->type] = $val; - } else { - - if (!is_array($data[$val->type])) { - - $data[$val->type] = [$data[$val->type]]; - } - - $data[$val->type][] = $val; - } - } - - foreach ($props as $k => $prop) { - - if ($name == $this->shorthand) { - - continue; - } - - $data[$k] = $prop[$index]; - } - - // match - $patterns = $this->config['pattern']; - - foreach ($patterns as $p => $pattern) { - - foreach (preg_split('#(\s+)#', $pattern, -1, PREG_SPLIT_NO_EMPTY) as $token) { - - if (empty($this->property_type[$token]['optional']) && (!isset($data[$token]) || (is_array($data[$token]) && !isset($data[$token][$index])))) { - - unset($patterns[$p]); - } - } - } - - if (empty($patterns)) { - - return $this; - } - - // - foreach ($data as $key => $val) { - - if (!is_array($val)) { - - $val = [$val]; - } - - $set = []; - - if (isset($properties[$key]['prefix'])) { - - $prefix = $properties[$key]['prefix']; - $set[] = (object)['type' => 'separator', 'value' => is_array($prefix) ? $prefix[1] : $prefix]; - } - - $set[] = $val[0]; - - // - if (Config::getPath('map.' . $key . '.multiple')) { - - $i = 0; - $j = count($val); - $sp = Config::getPath('map.' . $key . '.separator', ' '); - - $sp = $sp == ' ' ? ['type' => 'whitespace'] : ['type' => 'separator', 'value' => $sp]; - - while (++$i < $j) { - - $set[] = clone((object)$sp); - $set[] = $val[$i]; - } - } - - $data[$key] = $set; - } - - $set = []; - - foreach (preg_split('#(\s+)#', $pattern, -1, PREG_SPLIT_DELIM_CAPTURE) as $token) { - - if (isset($data[$token]) && isset($properties[$token]['prefix']) && is_array($properties[$token]['prefix'])) { - - $res = $set; - $j = count($res); - - while ($j--) { - - if (in_array($res[$j]->type, ['whitespace', 'separator'])) { - - continue; - } - - if ((isset($properties[$token]['multiple']) && $res[$j]->type == $token) || - $res[$j]->type == $properties[$token]['prefix'][0]['type']) { - - break; - } - - if ($res[$j]->type != $properties[$token]['prefix'][0]['type']) { - - return $this; - } - } - } - - if (trim($token) == '') { - - $set[] = (object)['type' => 'whitespace']; - } else if (isset($data[$token])) { - - array_splice($set, count($set), 0, $data[$token]); - } - } - - $results[] = $set; - } - - $set = []; - - $i = 0; - $j = count($results); - - array_splice($set, count($set), 0, $results[0]); - - while (++$i < $j) { - $set[] = (object)['type' => 'separator', 'value' => $separator]; - - array_splice($set, count($set), 0, $results[$i]); - } - - $data = Value::reduce($set, ['remove_defaults' => true]); - - if (empty($data)) { - - $this->properties[$name] = (new Property($name))->setValue($value); - return $this; - } - - $this->properties = [$this->shorthand => (new Property($this->shorthand))->setValue($data)-> - setLeadingComments($leadingcomments)-> - setTrailingComments($trailingcomments)]; - - return $this; - } - - /** - * set property - * @param string $name - * @param string $value - * @return PropertyMap - * @ignore - */ - protected function setProperty($name, $value) - { - - if (!isset($this->properties[$name])) { - - $this->properties[$name] = new Property($name); - } - - $this->properties[$name]->setValue($value); - - return $this; - } - - /** - * return Property array - * @return Property[] - */ - public function getProperties() - { - - return array_values($this->properties); - } - - /** - * convert this object to string - * @param string $join - * @return string - */ - public function render($join = "\n") - { - $glue = ';'; - $value = ''; - - foreach ($this->properties as $property) { - - $value .= $property->render() . $glue . $join; - } - - return rtrim($value, $glue . $join); - } - - /** - * @return bool - */ - - public function isEmpty() - { - - return empty($this->properties); - } - - /** - * convert this object to string - * @return string - */ - public function __toString() - { - - return $this->render(); - } -} \ No newline at end of file diff --git a/src/Property/PropertySet.php b/src/Property/PropertySet.php deleted file mode 100755 index 27b58618..00000000 --- a/src/Property/PropertySet.php +++ /dev/null @@ -1,549 +0,0 @@ -shorthand = $shorthand; - - foreach ($config['properties'] as $property) { - - $config[$property] = Config::getProperty($property); - $this->property_type[$config[$property]['type']][] = $property; - - unset($config[$property]['shorthand']); - } - - $this->config = $config; - - if (isset($config['pattern']) && is_array($config['pattern'])) { - - $this->config['pattern'] = $config['pattern'][0]; - } - } - - // @todo vendor prefix support - public function has($property): bool - { - - return isset($this->properties[$property]); - } - - // @todo vendor prefix support - public function remove($property): static - { - - unset($this->properties[$property]); - - return $this; - } - - /** - * set property value - * @param string $name - * @param array|string $value - * @param array|null $leadingcomments - * @param array|null $trailingcomments - * @param null $vendor - * @return PropertySet - */ - public function set(string $name, $value, ?array $leadingcomments = null, ?array $trailingcomments = null, $vendor = null, $src = null) - { - - $propertyName = ($vendor ? '-' . $vendor . '-' : '') . $name; - - // is valid property - if (($this->shorthand != $propertyName) && !in_array($propertyName, $this->config['properties'])) { - - throw new InvalidArgumentException('Invalid property ' . $propertyName . ' : ' . $this->shorthand, 400); - } - - // $name is shorthand -> expand - if ($this->shorthand == $propertyName) { - - foreach ($this->config['properties'] as $property) { - - unset($this->properties[$property]); - } - - if (is_string($value)) { - - $value = Value::parse($value, $name); - } - - $result = $this->expand($value); - - if ($result === false) { - - $this->setProperty($name, $value, $vendor, $src); - return $this; - } - - if (is_array($result)) { - - $this->expandProperties($result, $leadingcomments, $trailingcomments, $vendor); - unset($this->properties[$this->shorthand]); - } else { - - $this->setProperty($name, $value, $vendor, $src); - - if (!is_null($leadingcomments)) { - - $this->properties[$propertyName]->setLeadingComments($leadingcomments); - } - - if (!is_null($trailingcomments)) { - - $this->properties[$propertyName]->setTrailingComments($trailingcomments); - } - } - - } else { - - if (isset($this->properties[$this->shorthand])) { - - $shorthandValue = $this->properties[$this->shorthand]->getValue(); - - $result = $this->expand($shorthandValue); - - if ($result !== false) { - - $this->expandProperties($result); - } else { - - foreach ($this->config['properties'] as $property) { - - $this->properties[$property] = (new Property($property))->setValue($shorthandValue); - } - } - - unset($this->properties[$this->shorthand]); - } - - $this->setProperty($name, $value, $vendor, $src); - - if (!is_null($leadingcomments)) { - - $this->properties[$propertyName]->setLeadingComments($leadingcomments); - } - - if (!is_null($trailingcomments)) { - - $this->properties[$propertyName]->setTrailingComments($trailingcomments); - } - } - - return $this; - } - - /** - * @param array $result - * @param array|null $leadingcomments - * @param array|null $trailingcomments - * @param string|null $vendor - * @return $this - */ - protected function expandProperties(array $result, ?array $leadingcomments = null, ?array $trailingcomments = null, $vendor = null) - { - - foreach ($result as $property => $values) { - - $this->setProperty($property, $values, $vendor); - - if (!is_null($leadingcomments)) { - - $this->properties[$property]->setLeadingComments($leadingcomments); - } - - if (!is_null($trailingcomments)) { - - $this->properties[$property]->setTrailingComments($trailingcomments); - } - } - - return $this; - } - - /** - * expand shorthand property - * @param array|string $value - * @return array|bool - * @ignore - */ - protected function expand($value) - { - - if (is_string($value)) { - - $value = Value::parse($value, $this->shorthand, true, '', '', true); - } - - $pattern = explode(' ', $this->config['pattern']); - $value_map = []; - $values = []; - $result = []; - - $separator = $this->config['separator'] ?? null; - $index = 0; - - foreach ($value as $v) { - - if ($v->type == 'css-string' && $v->value == '!important') { - - return false; - } - - if (isset($v->value) && $v->value == $separator) { - - $index++; - } else { - - $value_map[$index][] = $v; - } - } - - $vmap = $value_map; - - foreach ($pattern as $match) { - - foreach ($value_map as $index => $map) { - - foreach ($map as $i => $v) { - - $className = Value::getClassName($v->type); - - if ($v->type == 'whitespace') { - - unset($vmap[$index][$i]); - } else if ($className::match($v, $match)) { - - $values[$index][$match][] = $v; - unset($vmap[$index][$i]); - break; - } - } - - if (empty($vmap[$index])) { - - unset($value_map[$index]); - } else { - - $value_map[$index] = $vmap[$index]; - } - } - } - - // does not match the pattern - if (!empty($value_map)) { - - return false; - } - - foreach ($values as $types) { - - foreach ($this->property_type as $unit => $properties) { - - foreach ($properties as $property) { - - // value not set - if (!isset($types[$unit])) { - - continue; - } - - $index = array_search($property, $this->property_type[$unit], true); - $key = null; - - $list = $types[$unit]; - - if (isset($list[$index])) { - - $key = $index; - } else { - - if (isset($this->config['value_map'][$property])) { - - foreach ($this->config['value_map'][$property] as $item) { - - if (isset($list[$item])) { - - $key = $item; - break; - } - } - } - } - - if (!is_null($key)) { - - $result[$property][] = $list[$key]; - } - } - } - } - - foreach ($result as $property => $values) { - - $i = count($values); - - if ($i > 0) { - - $separator = $this->config[$property]['separator'] ?? ' '; - - $separator = (object)($separator == ' ' ? [ - - 'type' => 'whitespace' - ] : - [ - 'type' => 'separator', - 'value' => $separator - ]); - - while ($i-- > 1) { - - array_splice($result[$property], $i, 0, [clone $separator]); - } - } - } - - return $result; - } - - /** - * set property - * @param string $name - * @param string $value - * @param null $vendor - * @return PropertySet - * @ignore - */ - protected function setProperty($name, $value, $vendor = null, $src = null) - { - - $propertyName = ($vendor && substr($name, 0, strlen($vendor) + 2) != '-' . $vendor . '-' ? '-' . $vendor . '-' : '') . $name; - - if (!isset($this->properties[$propertyName])) { - - $this->properties[$propertyName] = new Property($name); - } - - if ($vendor !== null) { - - $this->properties[$propertyName]->setVendor($vendor); - } - - if ($src !== null) { - - $this->properties[$propertyName]->setSrc($src); - } - - $this->properties[$propertyName]->setValue($value); - - return $this; - } - - /** - * return Property array - * @return Property[] - * @throws \Exception - */ - public function getProperties(): array - { - - $properties = $this->properties; - - foreach ($properties as $property) { - - foreach ($property->getValue() as $value) { - - if ($value->type == 'css-string' && $value->value == '!important') { - - return array_values($properties); - } - } - } - - if (isset($this->config['value_map']) && count($this->config['properties']) == count($properties)) { - - $all = []; - $keys = array_keys($properties); - - foreach ($keys as $property) { - - $all[$property] = Value::splitValues($properties[$property]->getValue(), $property); - } - - $count = count(current($all)); - - foreach ($all as $item) { - - if (count($item) != $count) { - - return array_values($properties); - } - } - - $result = []; - $separator = Config::getProperty($this->config['shorthand'] . '.separator', ' '); - - for ($i = 0; $i < $count; $i++) { - - $values = []; - - foreach ($keys as $key) { - - $values[$key] = $all[$key][$i]; - } - - foreach ($this->config['value_map'] as $property => $set) { - - $prop = $this->config['properties'][$set[0]]; - - if (!isset($properties[$property]) || !isset($properties[$prop])) { - - break; - } - - $v1 = $values[$property]; - $v2 = $values[$prop]; - - if (count($v1) == count($v2) && Value::equals($v1, $v2)) { - - unset($values[$property]); - continue; - } - - break; - } - - $result[$i] = $values; - } - - $token = (object)['type' => $separator == ' ' ? 'whitespace' : 'separator']; - - if ($token->type == 'separator') { - - $token->value = $separator; - } - - $values = []; - - $v = []; - foreach ($result[0] as $val) { - - array_splice($v, count($v), 0, $val); - } - - $l = count($v); - - while ($l-- > 1) { - - array_splice($v, $l, 0, [(object)['type' => 'whitespace']]); - } - - array_splice($values, count($values), 0, $v); - - $j = count($result); - - for ($i = 1; $i < $j; $i++) { - - $values[] = clone $token; - - $v = []; - foreach ($result[$i] as $val) { - - array_splice($v, count($v), 0, $val); - } - - $l = count($v); - - while ($l-- > 1) { - - array_splice($v, $l, 0, [(object)['type' => 'whitespace']]); - } - - array_splice($values, count($values), 0, $v); - } - - return [(new Property($this->shorthand))->setValue($values)]; - } - - return array_values($properties); - } - - /** - * convert this object to string - * @param string $join - * @return string - * @throws \Exception - */ - public function render($join = "\n"): string - { - $glue = ';'; - $value = ''; - - foreach ($this->getProperties() as $property) { - - $value .= $property->render() . $glue . $join; - } - - return rtrim($value, $glue . $join); - } - - public function isEmpty() - { - - return empty($this->properties); - } - - /** - * convert this object to string - * @return string - * @throws \Exception - */ - public function __toString() - { - - return $this->render(); - } -} \ No newline at end of file diff --git a/src/Property/PropertyTrait.php b/src/Property/PropertyTrait.php deleted file mode 100755 index 9f1313b9..00000000 --- a/src/Property/PropertyTrait.php +++ /dev/null @@ -1,42 +0,0 @@ -src = $src; - return $this; - } - - /** - * @return string|null - */ - public function getSrc() { - - return $this->src; - } - - /** - * @inheritDoc - */ - public function toObject() - { - return (object) array_filter(get_object_vars($this), function ($value) { - - return !is_null($value); - }); - } -} \ No newline at end of file diff --git a/src/Query/Evaluator.php b/src/Query/Evaluator.php index 7ac5c196..3f3cb30b 100755 --- a/src/Query/Evaluator.php +++ b/src/Query/Evaluator.php @@ -21,7 +21,6 @@ public function evaluate(string $expression, QueryInterface $context): array { $tokenList = (new Parser())->parse($expression); - return $this->sortNodes($tokenList->filter([$context])); } @@ -80,7 +79,7 @@ public function evaluateByClassName(string $classNames, QueryInterface $context) else if ($node->getType() == 'AtRule') { $value = $node->getValue(); - if ($this->search($selectors, [trim('@'.$node->getName().' '.(is_string($value) ? $value : Value::renderTokens($node->getValue(), ['remove_comments' => true])))])) { + if ($this->search($selectors, [trim('@'.$node->getName().' '.$node->getValue())])) { $result[] = $node; } diff --git a/src/Query/TokenSelectorValueAttributeExpression.php b/src/Query/TokenSelectorValueAttributeExpression.php index 707a57af..6e05ba16 100755 --- a/src/Query/TokenSelectorValueAttributeExpression.php +++ b/src/Query/TokenSelectorValueAttributeExpression.php @@ -48,13 +48,33 @@ public function __construct(array $value) public function evaluate(array $context): array { $result = []; + $key = $this->value[0]->type == 'attribute_name' ? $this->value[0]->value : ($this->value[2]->type == 'attribute_name' ? $this->value[2]->value : null); + + if ($key === null) { + + if ($this->value[0]->value == $this->value[2]->value) { + + return $context; + } + + return []; + } + + $value1 = $this->value[0]->type != 'attribute_name' ? $this->value[0]->value : ($this->value[2]->type != 'attribute_name' ? $this->value[2]->value : null); foreach ($context as $element) { - $value1 = $this->value[0]->type == 'attribute_name' ? (string) $element[$this->value[0]->value] : $this->value[0]->value; - $value2 = $this->value[2]->type == 'attribute_name' ? (string) $element[$this->value[2]->value] : $this->value[2]->value; + $value2 = (string) $element[$key]; $match = false; + if (in_array($value2[0] ?? null, ['"', "'"])) { + + if (preg_match('#^'. $value2[0].'[\w_-]+'. $value2[0].'$#', $value2)) { + + $value2 = substr($value2, 1, -1); + } + } + switch ($this->value[1]->value) { case '=': @@ -64,17 +84,17 @@ public function evaluate(array $context): array case '^=': - $match = substr($value1, 0, strlen($value2)) === $value2; + $match = str_starts_with($value2, $value1); break; case '*=': - $match = strpos($value1, $value2) !== false; + $match = str_contains($value2, $value1); break; case '$=': - $match = substr($value1, - strlen($value2)) === $value2; + $match = str_ends_with($value2, $value1); break; } @@ -91,7 +111,8 @@ public function evaluate(array $context): array * @param array $options * @return string */ - public function render(array $options = []) { + public function render(array $options = []): string + { $result = ''; diff --git a/src/Query/TokenSelectorValueAttributeFunction.php b/src/Query/TokenSelectorValueAttributeFunction.php index 4443b80a..d03e2804 100755 --- a/src/Query/TokenSelectorValueAttributeFunction.php +++ b/src/Query/TokenSelectorValueAttributeFunction.php @@ -64,8 +64,8 @@ public function evaluate(array $context): array /** * @inheritDoc */ - public function render(array $options) - { + public function render(array $options): string + { return $this->expression->render($options); } } \ No newline at end of file diff --git a/src/Query/TokenSelectorValueAttributeFunctionColor.php b/src/Query/TokenSelectorValueAttributeFunctionColor.php index 33ef99d8..f9702c65 100755 --- a/src/Query/TokenSelectorValueAttributeFunctionColor.php +++ b/src/Query/TokenSelectorValueAttributeFunctionColor.php @@ -5,107 +5,134 @@ use InvalidArgumentException; use TBela\CSS\Value; +/** + */ class TokenSelectorValueAttributeFunctionColor implements TokenSelectorValueInterface { - use FilterTrait; + use FilterTrait; - protected array $value = []; + protected array $value = []; + protected static array $cache = []; - /** - * TokenSelectorValueAttributeExpression constructor. - * @param array $value - */ - public function __construct($value) - { - if (count($value->arguments) != 3) { + /** + * TokenSelectorValueAttributeExpression constructor. + * @param array $value + * @throws \Exception + */ + public function __construct($value) + { + if (count($value->arguments) != 3) { - $value->arguments = $this->trim($value->arguments); - } + $value->arguments = $this->trim($value->arguments); + } - if (isset($value->arguments[1]) && $value->arguments[1]->type == 'separator' && $value->arguments[1]->value == ',') { + if (isset($value->arguments[1]) && $value->arguments[1]->type == 'separator' && $value->arguments[1]->value == ',') { - $value->arguments[1]->type = 'operator'; - $value->arguments[1]->value = '='; - } + $value->arguments[1]->type = 'operator'; + $value->arguments[1]->value = '='; + } - if (count($value->arguments) != 3) { + if (count($value->arguments) != 3) { - throw new InvalidArgumentException('expecting an array with 2 items', 400); - } + throw new InvalidArgumentException('expecting an array with 2 items', 400); + } - $value = $value->arguments; + $value = $value->arguments; - if (!in_array($value[0]->type, ['attribute_name', 'string']) || - $value[1]->type != 'operator' || - !in_array($value[2]->type, ['attribute_name', 'string'])) { + if (!in_array($value[0]->type, ['attribute_name', 'string']) || + $value[1]->type != 'operator' || + !in_array($value[2]->type, ['attribute_name', 'string'])) { - throw new InvalidArgumentException('invalid input', 400); - } + throw new InvalidArgumentException('invalid input', 400); + } - if ($value[1]->value != '=') { + if ($value[1]->value != '=') { - throw new InvalidArgumentException(sprintf('unsupported operator "%s"', $value[1]->value), 400); - } + throw new InvalidArgumentException(sprintf('unsupported operator "%s"', $value[1]->value), 400); + } - $options = [ - 'compress' => true, - 'convert_color' => 'hex', - 'css_level' => 4 - ]; + $options = [ + 'compress' => true, + 'convert_color' => 'hex', + 'css_level' => 4 + ]; - if ($value[0]->type == 'string') { + if ($value[0]->type == 'string') { - $value[0]->value = Value::renderTokens(Value::parse($value[0]->value, 'color', true, '', '', true), $options); - } + $value[0]->value = Value::renderTokens(Value::parse($value[0]->value, 'color', preserve_quotes: true), $options); + } - if ($value[2]->type == 'string') { + if ($value[2]->type == 'string') { - $value[2]->value = Value::renderTokens(Value::parse($value[2]->value, 'color', true, '', '', true), $options); - } + $value[2]->value = Value::renderTokens(Value::parse($value[2]->value, 'color', preserve_quotes: true), $options); + } - $this->value = $value; - } + $this->value = $value; + } - /** - * @inheritDoc - */ - public function evaluate(array $context): array - { - $result = []; + /** + * @inheritDoc + * @throws \Exception + */ + public function evaluate(array $context): array + { + $result = []; - $options = [ - 'compress' => true, - 'convert_color' => 'hex', - 'css_level' => 4 - ]; + foreach ($context as $element) { - foreach ($context as $element) { + if(!str_contains((string) $element['name'], 'color')) { - $value1 = $this->value[0]->type == 'attribute_name' ? $element[$this->value[0]->value] : $this->value[0]->value; - $value2 = $this->value[2]->type == 'attribute_name' ? $element[$this->value[2]->value] : $this->value[2]->value; + continue; + } - if ($value1 === $value2) { + $value1 = $this->value[0]->type == 'attribute_name' ? $element[$this->value[0]->value] : $this->value[0]->value; + $value2 = $this->value[2]->type == 'attribute_name' ? $element[$this->value[2]->value] : $this->value[2]->value; - $result[] = $element; - } - } + if ($value1 === $value2 || $this->toHexColor($value1) == $this->toHexColor($value2)) { - return $result; - } + $result[] = $element; + } + } - /** - * @inheritDoc - */ - public function render(array $options) - { + return $result; + } - $result = 'color('. - Value::renderTokens(Value::parse($this->value[0]->value, 'color', true, '', '', true), $options) - .','. - Value::renderTokens(Value::parse($this->value[2]->value, 'color', true, '', '', true), $options) - . - ')'; + /** + * @throws \Exception + */ + protected static function toHexColor(?string $color) + { - return $result; - } + if (is_null($color)) { + + return null; + } + + if (!isset(static::$cache[$color])) { + + static::$cache[$color] = Value::renderTokens(Value::parse($color), [ + 'compress' => true, + 'convert_color' => 'hex', + 'css_level' => 4 + ]); + } + + return static::$cache[$color]; + } + + /** + * @inheritDoc + */ + public function render(array $options) + { + + $result = 'color(' . + Value::renderTokens(Value::parse($this->value[0]->value, property: 'color', preserve_quotes: true), $options) + . ',' . + Value::renderTokens(Value::parse($this->value[2]->value, 'color', preserve_quotes: true), $options) + . + ')'; + + return $result; + } } \ No newline at end of file diff --git a/src/Query/TokenSelectorValueAttributeFunctionComment.php b/src/Query/TokenSelectorValueAttributeFunctionComment.php index f5fff6c8..6d310fc2 100755 --- a/src/Query/TokenSelectorValueAttributeFunctionComment.php +++ b/src/Query/TokenSelectorValueAttributeFunctionComment.php @@ -15,6 +15,8 @@ public function evaluate(array $context): array { return array_filter($context, function (QueryInterface $element) { +// var_dump($element['type']); + return $element['type'] == 'Comment'; }); } @@ -22,8 +24,8 @@ public function evaluate(array $context): array /** * @inheritDoc */ - public function render(array $options) - { + public function render(array $options): string + { return 'comment()'; } } \ No newline at end of file diff --git a/src/Query/TokenSelectorValueAttributeSelector.php b/src/Query/TokenSelectorValueAttributeSelector.php index 72fbe72e..88b4fa86 100755 --- a/src/Query/TokenSelectorValueAttributeSelector.php +++ b/src/Query/TokenSelectorValueAttributeSelector.php @@ -66,7 +66,7 @@ public function evaluate(array $context): array continue; } - $attr = is_string($val) ? $val : Value::renderTokens([$val]); + $attr = $val; switch ($this->value[1]->value) { diff --git a/src/Renderer.php b/src/Renderer.php index 809e3d8d..a36f68a4 100755 --- a/src/Renderer.php +++ b/src/Renderer.php @@ -6,14 +6,14 @@ use Exception; use stdClass; use TBela\CSS\Ast\Traverser; +use TBela\CSS\Element\Declaration\ValueList; use TBela\CSS\Exceptions\IOException; +use TBela\CSS\Interfaces\ElementInterface; use TBela\CSS\Interfaces\ParsableInterface; use TBela\CSS\Interfaces\RenderableInterface; -use TBela\CSS\Interfaces\ElementInterface; use TBela\CSS\Parser\Helper; use TBela\CSS\Parser\SyntaxError; use TBela\CSS\Process\Pool as ProcessPool; -use TBela\CSS\Property\PropertyList; use function is_string; /** @@ -256,7 +256,7 @@ public function renderAst(stdClass|ParsableInterface $ast, ?int $level = null): case 'Rule': case 'AtRule': case 'Comment': - case 'Property': +// case 'Property': case 'NestingRule': case 'Declaration': case 'NestingAtRule': @@ -633,32 +633,26 @@ protected function renderCollection(object $ast, ?int $level): string if (($this->options['compute_shorthand'] || !$this->options['allow_duplicate_declarations']) && $glue == ';') { $children = []; - $properties = new PropertyList(null, $this->options); - - foreach ($ast->children ?? [] as $child) { - - if (!empty($children)) { + $properties = new ValueList(null, $this->options); - $children[] = $child; - } else if ($child->type == 'Declaration' || $child->type == 'Comment') { + $childList = $ast->children ?? []; - $name = $child->name ?? null; + foreach ($childList as $key => $child) { - if (isset($name) && !$this->options['allow_duplicate_declarations'] && $properties->has($name)) { + if ($child->type == 'Declaration' || $child->type == 'Comment') { - $properties->remove($name); - } - - $properties->set($name, $child->value, $child->type, $child->leadingcomments ?? null, $child->trailingcomments ?? null, $child->src ?? null, $child->vendor ?? null); + $properties->add($child); } else { - $children[] = $child; + $children = array_merge($properties, array_slice($childList, $key)); + $properties = null; + break; } } - if (!$properties->isEmpty()) { + if (!$properties?->isEmpty() && empty($children)) { - array_splice($children, 0, 0, iterator_to_array($properties->getProperties())); + $children = $properties; } } else { @@ -810,9 +804,9 @@ protected function renderSelector(object $ast, ?int $level): string $selector = $ast->selector; - if (!isset($selector)) { + if (!isset($selector) || $selector === '') { - throw new Exception('The selector cannot be empty', 400); + throw new Exception(sprintf('the selector is empty: %s:%s:%s', $ast->src ?? '', $ast->position->line ?? '', $ast->position->column ?? ''), 400); } settype($level, 'int'); @@ -822,28 +816,31 @@ protected function renderSelector(object $ast, ?int $level): string $this->indents[$level] = str_repeat($this->options['indent'], $level); } +// if ($selector === '') { +// +// // the selector is empty! +// throw new Exception(sprintf('the selector is empty: %s:%s:%s', $ast->src ?? '', $ast->position->line ?? '', $ast->position->column ?? ''), 400); +// } + $indent = $this->indents[$level]; if (is_array($selector)) { - if (empty($selector)) { - - // the selector is empty! - throw new Exception(sprintf('the selector is empty: %s:%s:%s', $ast->src ?? '', $ast->position->line ?? '', $ast->position->column ?? ''), 400); - } - if (is_string($selector[0])) { - $selector = implode(',' . $this->options['indent'], $selector); + $result = $indent.implode(",".$this->options['glue'] . $indent, $selector); } } - if (is_string($selector)) { + else { - $selector = Value::parse($selector); - } +// if (is_string($selector)) { + + $selector = Value::parse($selector); +// } - $result = $indent . Value::renderTokens($selector, ['omit_unit' => false, 'compress' => $this->options['compress']], $this->options['glue'] . $indent); + $result = $indent . Value::renderTokens($selector, ['omit_unit' => false, 'compress' => $this->options['compress']], $this->options['glue'] . $indent); + } if ($ast->type == 'NestingAtRule' && !$this->options['legacy_rendering']) { @@ -865,20 +862,6 @@ protected function renderSelector(object $ast, ?int $level): string return $result; } - /** - * @param stdClass $ast - * @param int|null $level - * @return string - * @throws Exception - * @ignore - */ - - protected function renderDeclaration(stdClass $ast, ?int $level): string - { - - return $this->renderProperty($ast, $level); - } - /** * render a property * @param object $ast @@ -888,22 +871,22 @@ protected function renderDeclaration(stdClass $ast, ?int $level): string * @ignore */ - protected function renderProperty(object $ast, ?int $level): string + protected function renderDeclaration(object $ast, ?int $level): string { - if ($ast->type == 'Comment') { - - return empty($this->options['compress']) ? '' : $ast->value; - } +// if ($ast->type == 'Comment') { +// +// return empty($this->options['compress']) ? '' : $ast->value; +// } $name = $this->renderName($ast); if (class_exists(Value::getClassName($ast->name)) || !is_string($ast->value)) { - $property = is_string($ast->value) ? Value::parse($ast->value, $ast->name) : $ast->value; + $property = $ast->tokens ?? (is_array($ast->value) ? $ast->value : Value::parse($ast->value, $ast->name)); $value = Value::renderTokens($property, array_merge(['property' => $name], $this->options)); } else { - $value = $ast->value; + $value = !empty($ast->tokens) ? Value::renderTokens($ast->tokens, array_merge(['property' => $name], $this->options)) : $ast->value; } if ($value == 'none') { diff --git a/src/Value.php b/src/Value.php index f9b8a3f6..20cd4626 100755 --- a/src/Value.php +++ b/src/Value.php @@ -7,9 +7,8 @@ use JsonSerializable; use stdClass; use TBela\CSS\Interfaces\ObjectInterface; -use TBela\CSS\Property\Config; -use TBela\CSS\Value\Number; use TBela\CSS\Parser\ParserTrait; +use TBela\CSS\Value\Number; use function mb_ord; /** @@ -23,196 +22,197 @@ */ abstract class Value implements JsonSerializable, ObjectInterface { - use ParserTrait; - - /** - * var stdClass; - * @ignore - */ - protected ?stdClass $data = null; - - /** - * @var array - * @ignore - */ - protected static array $defaults = []; - - /** - * @var array - * @ignore - */ - protected static array $keywords = []; - - /** - * @var string|null - * @ignore - */ - protected ?string $hash = null; - - /** - * @var array - * @ignore - */ - protected static array $cache = []; - -// abstract public static function doRender(object $data, array $options = []); - - /** - * Value constructor. - * @param object $data - */ - protected function __construct(object $data) - { - - $this->data = $data; - } - - /** - * @throws Exception - */ - public static function renderTokens(array $tokens, array $options = [], $join = null): string + use ParserTrait; + + /** + * var stdClass; + * @ignore + */ + protected ?stdClass $data = null; + + /** + * @var array + * @ignore + */ + protected static array $defaults = []; + + /** + * @var array + * @ignore + */ + protected static array $keywords = []; + + /** + * @var string|null + * @ignore + */ + protected ?string $hash = null; + + /** + * @var array + * @ignore + */ + protected static array $cache = []; + + abstract public static function doRender(object $data, array $options = []): string; + + /** + * Value constructor. + * @param object $data + */ + protected function __construct(object $data) + { + + $this->data = $data; + } + + /** + * @throws Exception + */ + public static function renderTokens(array $tokens, array $options = [], $join = null): string { - $result = ''; + $result = ''; - foreach ($tokens as $token) { + foreach ($tokens as $token) { - if (!is_object($token)) { + if (!is_object($token)) { - throw new Exception(sprintf('invalid token %s', var_export($token, true))); - } + throw new Exception(sprintf('invalid token %s', var_export($token, true))); + } - switch ($token->type) { + switch ($token->type) { - case 'font': - case 'operator': - case 'separator': - case 'background': - case 'font-style': - case 'font-family': - case 'font-variant': - case 'outline-style': - case 'background-clip': - case 'background-size': - case 'background-repeat': - case 'background-attachment': + case 'font': + case 'operator': + case 'separator': + case 'background': + case 'font-style': + case 'font-family': + case 'font-variant': + case 'outline-style': + case 'background-clip': + case 'background-size': + case 'background-repeat': + case 'background-attachment': - $result .= $token->value; + $result .= $token->value; - if (isset($token->unit) && $token->value !== '0') { + if (isset($token->unit) && $token->value !== '0') { - $result .= $token->unit; - } + $result .= $token->unit; + } - if ($token->value == ',' && empty($options['compress'])) { + if ($token->value == ',' && empty($options['compress'])) { + + $result .= $join ?? ' '; + } - $result .= $join ?? ' '; - } + break; - break; - case 'whitespace': + case 'whitespace': - $result .= ' '; - break; + $result .= ' '; + break; - case 'css-src-format': + case 'css-src-format': - $result .= $token->name . '("' . Value::renderTokens($token->arguments) . '")'; - break; + $result .= $token->name . '("' . Value::renderTokens($token->arguments) . '")'; + break; - case 'css-function': - case 'invalid-css-function': - case 'css-parenthesis-expression': + case 'css-function': + case 'invalid-css-function': + case 'css-parenthesis-expression': - $result .= $token->name . '(' . Value::renderTokens($token->arguments, $options) . ')'; - break; + $result .= $token->name . '(' . Value::renderTokens($token->arguments, $options) . ')'; + break; - case 'unit': - case 'color': - case 'number': - case 'css-url': - case 'font-size': - case 'css-string': - case 'line-height': - case 'font-weight': - case 'outline-color': - case 'css-attribute': + case 'unit': + case 'color': + case 'number': + case 'css-url': + case 'font-size': + case 'css-string': + case 'line-height': + case 'font-weight': + case 'outline-color': + case 'css-attribute': // case 'css-src-format': - case 'outline-width': - case 'background-color': - case 'background-image': - case 'background-origin': - case 'background-position': - - $className = static::getClassName($token->type); - /** - * @var Value $className - */ - $result .= $className::doRender($token, $options); - break; - case 'Comment': - $result .= $token->value; - break; - default: - throw new Exception(sprintf("Not implemented: %s:\n%s", $token->type, var_export($token, true)), 501); - } - } - - return $result; - } - - /** - * @param stdClass $type - * @param bool $preserve_quotes - * @return void - */ - private static function parseString(stdClass $type, bool $preserve_quotes): void - { - if (!$preserve_quotes && strlen($type->value) > 2 && - in_array($type->value[0], ['"', "'"]) && - $type->value[0] == substr($type->value, -1)) { - - $value = substr($type->value, 1, -1); - - if (!preg_match('#^\d#', $value) && - preg_match('#^[\w_-]+$#', $value)) { - - $type->value = $value; - } - } - } - - /** - * get property - * @param string $name - * @return mixed|null - * @ignore - */ - public function __get(string $name) - { - if (isset($this->data->{$name})) { - - return $this->data->{$name}; - } - - if (is_callable([$this, 'get' . $name])) { - - return call_user_func([$this, 'get' . $name]); - } - - return null; - } - - /** - * @param string $name - * @return bool - * @ignore - */ - public function __isset(string $name) - { - - return isset($this->data->{$name}); - } + case 'outline-width': + case 'background-color': + case 'background-image': + case 'background-origin': + case 'background-position': + + $className = static::getClassName($token->type); + /** + * @var Value $className + */ + $result .= $className::doRender($token, $options); + break; + case 'Comment': + $result .= $token->value; + break; + default: + throw new Exception(sprintf("Not implemented: %s:\n%s", $token->type, var_export($token, true)), 501); + } + } + + return $result; + } + + /** + * @param stdClass $type + * @param bool $preserve_quotes + * @return void + */ + private static function parseString(stdClass $type, bool $preserve_quotes): void + { + if (!$preserve_quotes && strlen($type->value) > 2 && + in_array($type->value[0], ['"', "'"]) && + $type->value[0] == substr($type->value, -1)) { + + $value = substr($type->value, 1, -1); + + if (!preg_match('#^\d#', $value) && + preg_match('#^[\w_-]+$#', $value)) { + + $type->value = $value; + } + } + } + + /** + * get property + * @param string $name + * @return mixed|null + * @ignore + */ + public function __get(string $name) + { + if (isset($this->data->{$name})) { + + return $this->data->{$name}; + } + + if (is_callable([$this, 'get' . $name])) { + + return call_user_func([$this, 'get' . $name]); + } + + return null; + } + + /** + * @param string $name + * @return bool + * @ignore + */ + public function __isset(string $name) + { + + return isset($this->data->{$name}); + } /** * test if this object matches the specified type @@ -220,146 +220,146 @@ public function __isset(string $name) * @param string $type * @return bool */ - public static function match(object $data, string $type): bool - { - - return strtolower($data->type) == $type; - } - - /** - * get the class name of the specified type - * @param string $type - * @return string - */ - public static function getClassName(string $type): string - { - - static $classNames = []; - - if (!isset($classNames[$type])) { - - $classNames[$type] = Value::class . '\\' . preg_replace_callback('#(^|-)([a-z])#', function ($matches) { - - return strtoupper($matches[2]); - }, $type); - } - - return $classNames[$type]; - } - - /** - * value type - * @return string - * @ignore - */ - protected static function type(): string - { - - static $types = []; - - if (!isset($types[static::class])) { - - $name = explode('\\', static::class); - - $types[static::class] = preg_replace_callback('#(^|[^A-Z])([A-Z])#', function ($matches) { - - return (empty($matches[1]) ? '' : $matches[1] . '-') . strtolower($matches[2]); - }, end($name)); - } - - return $types[static::class]; - } - - /** - * @param object $token - * @return bool - * @ignore - */ - protected static function matchDefaults(object $token): bool - { - - return isset($token->value) && in_array(strtolower($token->value), static::$defaults); - } - - /** - * @param object $token - * @param object|null $previousToken - * @param object|null $previousValue - * @param object|null $nextToken - * @param object|null $nextValue - * @param int|null $index - * @param array $tokens - * @return bool - */ - public static function matchToken(object $token, object $previousToken = null, object $previousValue = null, object $nextToken = null, object $nextValue = null, int $index = null, array $tokens = []): bool - { - - return $token->type == static::type() || isset($token->value) && static::matchKeyword($token->value); - } - - /** - * test if $data matches this class - * @param stdClass $data - * @return bool - */ - protected static function validate(stdClass $data): bool - { - - return isset($data->value); - } - - /** - * create an instance - * @param stdClass $data - * @return Value - */ - public static function getInstance(stdClass $data): Value - { + public static function match(object $data, string $type): bool + { + + return strtolower($data->type) == $type; + } + + /** + * get the class name of the specified type + * @param string $type + * @return string + */ + public static function getClassName(string $type): string + { + + static $classNames = []; + + if (!isset($classNames[$type])) { + + $classNames[$type] = Value::class . '\\' . preg_replace_callback('#(^|-)([a-z])#', function ($matches) { + + return strtoupper($matches[2]); + }, $type); + } + + return $classNames[$type]; + } + + /** + * value type + * @return string + * @ignore + */ + protected static function type(): string + { + + static $types = []; + + if (!isset($types[static::class])) { + + $name = explode('\\', static::class); + + $types[static::class] = preg_replace_callback('#(^|[^A-Z])([A-Z])#', function ($matches) { + + return (empty($matches[1]) ? '' : $matches[1] . '-') . strtolower($matches[2]); + }, end($name)); + } + + return $types[static::class]; + } + + /** + * @param object $token + * @return bool + * @ignore + */ + protected static function matchDefaults(object $token): bool + { + + return isset($token->value) && in_array(strtolower($token->value), static::$defaults); + } + + /** + * @param object $token + * @param object|null $previousToken + * @param object|null $previousValue + * @param object|null $nextToken + * @param object|null $nextValue + * @param int|null $index + * @param array $tokens + * @return bool + */ + public static function matchToken(object $token, object $previousToken = null, object $previousValue = null, object $nextToken = null, object $nextValue = null, int $index = null, array $tokens = []): bool + { + + return $token->type == static::type() || isset($token->value) && static::matchKeyword($token->value); + } + + /** + * test if $data matches this class + * @param stdClass $data + * @return bool + */ + protected static function validate(stdClass $data): bool + { + + return isset($data->value); + } + + /** + * create an instance + * @param stdClass $data + * @return Value + */ + public static function getInstance(stdClass $data): Value + { if (!isset($data->type)) { - throw new InvalidArgumentException('Type property is required: ' . gettype($data) . ':' . var_export($data, true), 400); - } + throw new InvalidArgumentException('Type property is required: ' . gettype($data) . ':' . var_export($data, true), 400); + } - $className = static::getClassName($data->type); + $className = static::getClassName($data->type); - if (!class_exists($className)) { + if (!class_exists($className)) { - error_log(__METHOD__ . ' missing data type? ' . $className); - $className = static::class; - } + error_log(__METHOD__ . ' missing data type? ' . $className); + $className = static::class; + } - if (!$className::validate($data)) { + if (!$className::validate($data)) { - throw new InvalidArgumentException('Invalid argument: $className:' . $className . ' data:' . var_export($data, true), 400); - } + throw new InvalidArgumentException('Invalid argument: $className:' . $className . ' data:' . var_export($data, true), 400); + } - return new $className($data); - } + return new $className($data); + } - /** - * convert this object to string - * @param array $options - * @return string - */ - public function render(array $options = []): string - { + /** + * convert this object to string + * @param array $options + * @return string + */ + public final function render(array $options = []): string + { - return $this->data->value; - } + throw new \RuntimeException('Not Implemented', 501); + } - /** - * compare parsed values - * @param array $value - * @param array $otherValue - * @return bool - * @throws Exception - */ - public static function equals(array $value, array $otherValue): bool - { + /** + * compare parsed values + * @param array $value + * @param array $otherValue + * @return bool + * @throws Exception + */ + public static function equals(array $value, array $otherValue): bool + { - return static::renderTokens($value, ['convert_color' => 'hex']) == static::renderTokens($otherValue, ['convert_color' => 'hex']); - } + return static::renderTokens($value, ['convert_color' => 'hex']) == static::renderTokens($otherValue, ['convert_color' => 'hex']); + } /** * avoid parsing string @@ -367,299 +367,304 @@ public static function equals(array $value, array $otherValue): bool * @param array|null $comments * @return string | bool */ - public static function format($string, ?array &$comments = null): bool|string + public static function format($string, ?array &$comments = null): bool|string { - $result = ''; + $result = ''; - $string = trim($string); - $j = strlen($string) - 1; - $i = -1; + $string = trim($string); + $j = strlen($string) - 1; + $i = -1; - while ($i++ < $j) { + while ($i++ < $j) { - switch ($string[$i]) { + switch ($string[$i]) { - case ' ': - case "\t": - case "\r": - case "\n": + case ' ': + case "\t": + case "\r": + case "\n": - while ($i + 1 < $j && static::is_whitespace($string[$i + 1])) { + while ($i + 1 < $j && static::is_whitespace($string[$i + 1])) { - $i++; - } + $i++; + } - if (($string[$i + 1] ?? ',') != ',') { + if (($string[$i + 1] ?? ',') != ',') { - $result .= ' '; - } + $result .= ' '; + } - break; + break; - case '"': - case "'": + case '"': + case "'": - $k = $i; + $k = $i; - while (++$k < $j) { + while (++$k < $j) { - if ($string[$k] == $string[$i] && $string[$k - 1] != '\\') { + if ($string[$k] == $string[$i] && $string[$k - 1] != '\\') { - break; - } - } + break; + } + } - if ($k > $j || $string[$k] != $string[$i]) { + if ($k > $j || $string[$k] != $string[$i]) { - // unterminated string - return false; - } else { + // unterminated string + return false; + } else { - $result .= substr($string, $i, $k - $i + 1); - } + $result .= substr($string, $i, $k - $i + 1); + } - $i = $k; + $i = $k; - break; + break; - case ',': + case ',': - $result .= ','; + $result .= ','; - while ($i < $j && static::is_whitespace($string[$i + 1])) { + while ($i < $j && static::is_whitespace($string[$i + 1])) { - $i++; - } + $i++; + } - break; + break; - case '[': - case '(': + case '[': + case '(': - $start = $string[$i] == '[' ? '[' : '('; - $end = $string[$i] == '[' ? ']' : ')'; + $start = $string[$i] == '[' ? '[' : '('; + $end = $string[$i] == '[' ? ']' : ')'; - $params = static::_close($string, $end, $start, $i, $j); + $params = static::_close($string, $end, $start, $i, $j); - if ($params === false) { + if ($params === false) { // throw new SyntaxError(sprintf('missing "%s"', $end), 400); - return false; + return false; - } else { + } else { - $res = static::format(substr($params, 1, -1), $comments); + $res = static::format(substr($params, 1, -1), $comments); - if ($res === false) { + if ($res === false) { - return false; - } + return false; + } - $result .= $start . $res . $end; - $i += strlen($params) - 1; - } + $result .= $start . $res . $end; + $i += strlen($params) - 1; + } - break; + break; - case '/': + case '/': - $comment = '/'; + $comment = '/'; - if ($i < $j && $string[$i + 1] == '*') { + if ($i < $j && $string[$i + 1] == '*') { - $comment .= '*'; - $i++; + $comment .= '*'; + $i++; - while ($i++ < $j) { + while ($i++ < $j) { - if ($string[$i] == '\\') { + if ($string[$i] == '\\') { - $comment .= '\\'; + $comment .= '\\'; - if ($i < $j) { + if ($i < $j) { - $comment .= $string[++$i]; - continue; - } - } + $comment .= $string[++$i]; + continue; + } + } - $comment .= $string[$i]; + $comment .= $string[$i]; - if ($i < $j && $string[$i] == '*' && $string[$i + 1] == '/') { + if ($i < $j && $string[$i] == '*' && $string[$i + 1] == '/') { - $comment .= $string[++$i]; + $comment .= $string[++$i]; - if (!is_null($comments)) { + if (!is_null($comments)) { - $comments[] = $comment; - while ($i < $j && static::is_whitespace($string[$i + 1])) { + $comments[] = $comment; + while ($i < $j && static::is_whitespace($string[$i + 1])) { - $i++; - } - } else { + $i++; + } + } else { - $result .= $comment; - } + $result .= $comment; + } - break 2; - } - } + break 2; + } + } // throw new SyntaxError("unterminated comment", 400); - return false; - } else { + return false; + } else { + + $result .= '/'; + } - $result .= '/'; - } + break; - break; + case '<': - case '<': + if (substr($string, $i, 4) == '') { - if ($string[$i] == '-' && $i < $j - 3 && substr($string, $i, 4) == '--!>') { + $comment .= '--!>'; + $i += 3; - $comment .= '--!>'; - $i += 3; + if (!is_null($comments)) { - if (!is_null($comments)) { + $comments[] = $comment; + } else { - $comments[] = $comment; - } else { + $result .= $comment; + } - $result .= $comment; - } + break 2; + } - break 2; - } + $comment .= $string[$i]; + } - $comment .= $string[$i]; - } + // invalid comment + return false; + } else { - // invalid comment - return false; - } else { + $result .= $string[$i]; + } - $result .= $string[$i]; - } + break; - break; + default: - default: + $result .= $string[$i]; + break; + } + } + + return rtrim($result); + } - $result .= $string[$i]; - break; - } - } + /** + * parse a css value + * @param string $string + * @param string|null $property + * @param bool $capture_whitespace + * @param string $context + * @param string $contextName + * @param bool $preserve_quotes + * @return array + */ + public static function parse(string $string, string $property = null, bool $capture_whitespace = true, string $context = '', string $contextName = '', bool $preserve_quotes = false): array + { - return rtrim($result); - } + if (trim($property) === '') { - /** - * parse a css value - * @param string $string - * @param string|null $property - * @param bool $capture_whitespace - * @param string $context - * @param string $contextName - * @param bool $preserve_quotes - * @return array - */ - public static function parse(string $string, string $property = null, bool $capture_whitespace = true, string $context = '', string $contextName = '', bool $preserve_quotes = false): array - { + $property = null; + } - if (trim($property) === '') { + $string = trim($string); + $property = strtolower($property); - $property = null; - } + if ($property == 'content' || $property == 'charset') { - $string = trim($string); - $property = strtolower($property); + $preserve_quotes = true; + } - if ($property !== '') { + if ($property !== '') { - $className = static::getClassName($property); + $className = static::getClassName($property); - if (is_callable([$className, 'doParse'])) { + if (is_callable([$className, 'doParse'])) { - try { + try { - return call_user_func([$className, 'doParse'], $string, $capture_whitespace, $context, $contextName, $preserve_quotes); - } catch (Exception) { + return call_user_func([$className, 'doParse'], $string, $capture_whitespace, $context, $contextName, $preserve_quotes); + } catch (Exception) { // throw $e; - // failed to parse css property - } - } - } + // failed to parse css property + } + } + } - return static::doParse($string, $capture_whitespace, $context, $preserve_quotes); - } + return static::doParse($string, $capture_whitespace, $context, $preserve_quotes); + } - /** - * remove unnecessary tokens - * @param array $tokens - * @param array $options - * @return array - */ - public static function reduce(array $tokens, array $options = []): array - { - $j = count($tokens); + /** + * remove unnecessary tokens + * @param array $tokens + * @param array $options + * @return array + */ + public static function reduce(array $tokens, array $options = []): array + { + $j = count($tokens); - if ($j > 1) { + if ($j > 1) { - while ($j--) { + while ($j--) { - $token = $tokens[$j]; + $token = $tokens[$j]; - if ($token->type == 'css-string' && $token->value === '') { + if ($token->type == 'css-string' && $token->value === '') { - array_splice($tokens, $j, 1); - continue; - } + array_splice($tokens, $j, 1); + continue; + } - if ($token->type == 'css-string' && $token->value == '!important' && count($tokens) <= 2) { + if ($token->type == 'css-string' && $token->value == '!important' && count($tokens) <= 2) { - break; - } + break; + } - if ($token->type == 'whitespace' && - isset($tokens[$j + 1]) && - (in_array($tokens[$j + 1]->type, ['separator', 'operator', 'whitespace']) || - $tokens[$j + 1]->type == 'css-string' && $tokens[$j + 1]->value == '!important') - ) { + if ($token->type == 'whitespace' && + isset($tokens[$j + 1]) && + (in_array($tokens[$j + 1]->type, ['separator', 'operator', 'whitespace']) || + $tokens[$j + 1]->type == 'css-string' && $tokens[$j + 1]->value == '!important') + ) { - array_splice($tokens, $j, 1); - } else if (in_array($token->type, ['separator', 'operator']) && isset($tokens[$j + 1]) && $tokens[$j + 1]->type == 'whitespace') { + array_splice($tokens, $j, 1); + } else if (in_array($token->type, ['separator', 'operator']) && isset($tokens[$j + 1]) && $tokens[$j + 1]->type == 'whitespace') { - array_splice($tokens, $j + 1, 1); - } else if (!empty($options['remove_defaults']) && !in_array($token->type, ['whitespace', 'separator'])) { + array_splice($tokens, $j + 1, 1); + } else if (!empty($options['remove_defaults']) && !in_array($token->type, ['whitespace', 'separator'])) { - // check if the previous token has the same type and matches the defaults - // same type? no -> remove - // same type? yes -> matches defaults? yes -> remove + // check if the previous token has the same type and matches the defaults + // same type? no -> remove + // same type? yes -> matches defaults? yes -> remove - $className = static::getClassName($token->type); + $className = static::getClassName($token->type); - if (is_callable($className . '::matchDefaults') && call_user_func($className . '::matchDefaults', $token)) { + if (is_callable($className . '::matchDefaults') && call_user_func($className . '::matchDefaults', $token)) { - if (in_array($token->type, ['background-size', 'background-repeat'])) { + if (in_array($token->type, ['background-size', 'background-repeat'])) { - if ((isset($tokens[$j - 2]) && $token->type == $tokens[$j - 2]->type) || - (isset($tokens[$j + 2]) && $token->type == $tokens[$j + 2]->type)) { + if ((isset($tokens[$j - 2]) && $token->type == $tokens[$j - 2]->type) || + (isset($tokens[$j + 2]) && $token->type == $tokens[$j + 2]->type)) { - continue; - } - } + continue; + } + } - $prefix = Config::getPath('map.' . $token->type . '.prefix'); + $prefix = Config::getPath('map.' . $token->type . '.prefix'); if (is_array($prefix)) { @@ -667,726 +672,726 @@ public static function reduce(array $tokens, array $options = []): array } // remove item - array_splice($tokens, $j, 1); + array_splice($tokens, $j, 1); - if (isset($tokens[$j]) && $tokens[$j]->type == 'whitespace') { + if (isset($tokens[$j]) && $tokens[$j]->type == 'whitespace') { - // remove whitespace after the item removed - array_splice($tokens, $j, 1); - } + // remove whitespace after the item removed + array_splice($tokens, $j, 1); + } - $key = $tokens[$j - 1] ?? null; + $key = $tokens[$j - 1] ?? null; - if (!is_null($key) && $key->type == 'separator' && $key->value == $prefix) { + if (!is_null($key) && $key->type == 'separator' && $key->value == $prefix) { - array_splice($tokens, --$j, 1); - } - } - } - } - } + array_splice($tokens, --$j, 1); + } + } + } + } + } - while (true) { + while (true) { - // remove leading whitespace - if (isset($tokens[0]) && $tokens[0]->type == 'whitespace') { + // remove leading whitespace + if (isset($tokens[0]) && $tokens[0]->type == 'whitespace') { - array_shift($tokens); - } else { + array_shift($tokens); + } else { - $count = count($tokens) - 1; + $count = count($tokens) - 1; - if (isset($tokens[$count]) && $tokens[$count]->type == 'whitespace') { + if (isset($tokens[$count]) && $tokens[$count]->type == 'whitespace') { - array_pop($tokens); - continue; - } + array_pop($tokens); + continue; + } - break; - } - } + break; + } + } - return $tokens; - } + return $tokens; + } - /** - * parse a css value - * @param string $string - * @param bool $capture_whitespace - * @param string $context + /** + * parse a css value + * @param string $string + * @param bool $capture_whitespace + * @param string $context * @param bool $preserve_quotes - * @return array - */ - protected static function doParse(string $string, bool $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): array + * @return array + */ + protected static function doParse(string $string, bool $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): array { - return static::reduce(static::getTokens($string, $capture_whitespace, $context, $preserve_quotes)); - } + return static::reduce(static::getTokens($string, $capture_whitespace, $context, $preserve_quotes)); + } - /** - * parse a css value - * @param string $string - * @param bool $capture_whitespace - * @param string $context + /** + * parse a css value + * @param string $string + * @param bool $capture_whitespace + * @param string $context * @param bool $preserve_quotes - * @return array|null - */ - public static function getTokens(string $string, bool $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): ?array + * @return array|null + */ + public static function getTokens(string $string, bool $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): ?array { - $string = mb_str_split(trim($string)); + $string = mb_str_split(trim($string)); - $i = -1; - $j = count($string) - 1; + $i = -1; + $j = count($string) - 1; - $buffer = ''; - $tokens = []; + $buffer = ''; + $tokens = []; - while (++$i <= $j) { + while (++$i <= $j) { - switch ($string[$i]) { + switch ($string[$i]) { - case "\0": + case "\0": - $buffer .= '\fffd'; - break; + $buffer .= '\fffd'; + break; - case '<': + case '<': - if (implode('', array_slice($string, $i, 4)) == '') { + $buffer .= $string[$k]; + if ($string[$k] == '-' && implode('', array_slice($string, $k, 3)) == '-->') { - $buffer .= '->'; + $buffer .= '->'; - $tokens[] = (object)[ - 'type' => 'Comment', - 'value' => $buffer - ]; + $tokens[] = (object)[ + 'type' => 'Comment', + 'value' => $buffer + ]; - $buffer = ''; - $i = $k + 2; - break 2; - } - } + $buffer = ''; + $i = $k + 2; + break 2; + } + } - // unclosed comment - $tokens[] = (object)[ - 'type' => 'invalid-comment', - 'value' => $buffer - ]; + // unclosed comment + $tokens[] = (object)[ + 'type' => 'invalid-comment', + 'value' => $buffer + ]; - $buffer = ''; - $i = $j; - break; - } + $buffer = ''; + $i = $j; + break; + } - $buffer .= '<'; - break; + $buffer .= '<'; + break; - case ';': + case ';': - if ($context == 'invalid-css-function') { + if ($context == 'invalid-css-function') { - $tokens[] = static::getType($buffer . ';'); - $buffer = ''; + $tokens[] = static::getType($buffer . ';'); + $buffer = ''; - } else if ($buffer !== '') { + } else if ($buffer !== '') { - $tokens[] = static::getType($buffer); - $buffer = ''; - } + $tokens[] = static::getType($buffer); + $buffer = ''; + } - break; + break; - case ' ': - case "\t": - case "\n": - case "\r": + case ' ': + case "\t": + case "\n": + case "\r": - if (rtrim($buffer) !== '') { + if (rtrim($buffer) !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - $buffer = ''; - } + $tokens[] = static::getType($buffer, $preserve_quotes); + $buffer = ''; + } - if ($capture_whitespace) { + if ($capture_whitespace) { - $k = $i; + $k = $i; - while (++$k <= $j) { + while (++$k <= $j) { - if (preg_match('#\s#', $string[$k])) { + if (preg_match('#\s#', $string[$k])) { - continue; - } + continue; + } - break; - } + break; + } - if ($k <= $j) { + if ($k <= $j) { - $token = new stdClass; - $token->type = 'whitespace'; - $tokens[] = $token; - } + $token = new stdClass; + $token->type = 'whitespace'; + $tokens[] = $token; + } - $i = $k - 1; - } + $i = $k - 1; + } - break; + break; - case '"': - case "'": + case '"': + case "'": - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $next = $i; + $next = $i; - while (true) { + while (true) { - $next = static::indexOf($string, $string[$i], $next + 1); + $next = static::indexOf($string, $string[$i], $next + 1); - if ($next !== false) { + if ($next !== false) { - if ($string[$next - 1] != '\\') { + if ($string[$next - 1] != '\\') { - break; - } - } else { + break; + } + } else { - break; - } - } + break; + } + } - $token = new stdClass; + $token = new stdClass; - if ($next === false) { + if ($next === false) { - $token->type = 'invalid-css-string'; - $token->value = implode('', array_slice($string, $i + 1)); - $token->q = $string[$i]; - } else { + $token->type = 'invalid-css-string'; + $token->value = implode('', array_slice($string, $i + 1)); + $token->q = $string[$i]; + } else { - $token->type = 'css-string'; - $token->value = implode('', array_slice($string, $i, $next - $i + 1)); + $token->type = 'css-string'; + $token->value = implode('', array_slice($string, $i, $next - $i + 1)); - self::parseString($token, $preserve_quotes); - } + self::parseString($token, $preserve_quotes); + } - if ($token->value !== '') { + if ($token->value !== '') { - $tokens[] = $token; - } + $tokens[] = $token; + } - $buffer = ''; + $buffer = ''; - if ($next === false) { + if ($next === false) { - $i = $j; - continue 2; - } + $i = $j; + continue 2; + } - $i = $next; + $i = $next; - break; + break; - case '\\': + case '\\': - $buffer .= $string[$i]; + $buffer .= $string[$i]; - if (isset($string[$i + 1])) { + if (isset($string[$i + 1])) { - $buffer .= $string[++$i]; - } + $buffer .= $string[++$i]; + } - break; + break; - case '[': + case '[': - $params = static::_close($string, ']', '[', $i, $j); + $params = static::_close($string, ']', '[', $i, $j); - if ($params !== false) { + if ($params !== false) { - if (trim($buffer) !== '') { + if (trim($buffer) !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $token = new stdClass; + $token = new stdClass; - $token->type = 'css-attribute'; - $token->arguments = Value::parse(substr($params, 1, -1), null, $capture_whitespace, 'attribute', '', $preserve_quotes); + $token->type = 'css-attribute'; + $token->arguments = Value::parse(substr($params, 1, -1), null, $capture_whitespace, 'attribute', '', $preserve_quotes); - $tokens[] = $token; + $tokens[] = $token; - $buffer = ''; - $i += strlen($params) - 1; + $buffer = ''; + $i += strlen($params) - 1; - } else { + } else { - $tokens[] = static::getType($buffer . implode('', array_slice($string, $i)), $preserve_quotes); - $buffer = ''; - $i = $j; - } + $tokens[] = static::getType($buffer . implode('', array_slice($string, $i)), $preserve_quotes); + $buffer = ''; + $i = $j; + } - break; - case '(': + break; + case '(': - $params = static::_close($string, ')', '(', $i, $j); + $params = static::_close($string, ')', '(', $i, $j); - if ($params !== false) { + if ($params !== false) { - $token = new stdClass; + $token = new stdClass; - if (preg_match('#^(-([a-zA-Z]+)-(\S+))#i', $buffer, $matches)) { + if (preg_match('#^(-([a-zA-Z]+)-(\S+))#i', $buffer, $matches)) { - $token->name = $matches[3]; - $token->vendor = $matches[2]; - } else { + $token->name = $matches[3]; + $token->vendor = $matches[2]; + } else { - $token->name = $buffer; - } + $token->name = $buffer; + } - if (in_array(strtolower($token->name), [ - 'rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk' //, 'lab', 'lch' // - ])) { + if (in_array(strtolower($token->name), [ + 'rgb', 'rgba', 'hsl', 'hsla', 'hwb', 'device-cmyk' //, 'lab', 'lch' // + ])) { - $token->type = 'color'; - } else if ($token->name == 'url') { + $token->type = 'color'; + } else if ($token->name == 'url') { - $token->type = 'css-url'; - } else if ($token->name == 'format') { + $token->type = 'css-url'; + } else if ($token->name == 'format') { - $token->type = 'css-src-format'; - } else { + $token->type = 'css-src-format'; + } else { - $token->type = $token->name === '' ? 'css-parenthesis-expression' : 'css-function'; - } + $token->type = $token->name === '' ? 'css-parenthesis-expression' : 'css-function'; + } - $str = substr($params, 1, -1); + $str = substr($params, 1, -1); - if ($buffer == 'url') { + if ($buffer == 'url') { - $t = new stdClass; + $t = new stdClass; - $t->type = 'css-string'; - $t->value = $str; + $t->type = 'css-string'; + $t->value = $str; - self::parseString($t, $preserve_quotes); + self::parseString($t, $preserve_quotes); - $token->arguments = [$t]; - } else { + $token->arguments = [$t]; + } else { - if (in_array($buffer, ['or', 'and'])) { + if (in_array($buffer, ['or', 'and'])) { - $token->name = ''; - $token->type = 'css-parenthesis-expression'; - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $token->name = ''; + $token->type = 'css-parenthesis-expression'; + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $token->arguments = Value::parse($str, null, $capture_whitespace, $token->type, $token->name, $preserve_quotes); - } + $token->arguments = Value::parse($str, null, $capture_whitespace, $token->type, $token->name, $preserve_quotes); + } - if (!empty($token->name)) { + if (!empty($token->name)) { - foreach ($token->arguments as $arg) { + foreach ($token->arguments as $arg) { - if ($arg->type == 'css-function') { + if ($arg->type == 'css-function') { - $token->type = 'css-function'; - break; - } - } - } + $token->type = 'css-function'; + break; + } + } + } - $tokens[] = $token; - $buffer = ''; + $tokens[] = $token; + $buffer = ''; - $i += strlen($params) - 1; - } else { + $i += strlen($params) - 1; + } else { - if ($buffer === '') { + if ($buffer === '') { - $tokens[] = static::getType($buffer . implode('', array_slice($string, $i)), $preserve_quotes); - } else { + $tokens[] = static::getType($buffer . implode('', array_slice($string, $i)), $preserve_quotes); + } else { - $token = (object)[ - 'type' => 'invalid-css-function', - 'name' => $buffer, - 'arguments' => [] - ]; + $token = (object)[ + 'type' => 'invalid-css-function', + 'name' => $buffer, + 'arguments' => [] + ]; - $args = implode('', array_slice($string, $i + 1)); + $args = implode('', array_slice($string, $i + 1)); - if (trim($args) !== '') { + if (trim($args) !== '') { - $token->arguments = Value::parse($args, '', '', $token->type, $token->name, $preserve_quotes); - } + $token->arguments = Value::parse($args, '', '', $token->type, $token->name, $preserve_quotes); + } - $tokens[] = $token; - } + $tokens[] = $token; + } - $buffer = ''; - $i = $j; - } + $buffer = ''; + $i = $j; + } - break; + break; - case '|': + case '|': - if (isset($string[$i + 1]) && $string[$i + 1] == '|') { + if (isset($string[$i + 1]) && $string[$i + 1] == '|') { - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - $buffer = ''; - } + $tokens[] = static::getType($buffer, $preserve_quotes); + $buffer = ''; + } - $token = end($tokens); + $token = end($tokens); - if (($token->type ?? '') == 'whitespace') { + if (($token->type ?? '') == 'whitespace') { - array_pop($tokens); - } + array_pop($tokens); + } - $tokens[] = (object)['type' => 'operator', 'value' => '||']; - $i++; - break; - } + $tokens[] = (object)['type' => 'operator', 'value' => '||']; + $i++; + break; + } - $buffer .= $string[$i]; - break; + $buffer .= $string[$i]; + break; - case '>': - case '+': + case '>': + case '+': - if ($context === '') { + if ($context === '') { - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $tokens[] = (object)['type' => 'separator', 'value' => $string[$i]]; - $buffer = ''; - break; - } + $tokens[] = (object)['type' => 'separator', 'value' => $string[$i]]; + $buffer = ''; + break; + } - if ($context == 'css-function' && trim($buffer) === '' && static::is_whitespace($string[$i + 1])) { + if ($context == 'css-function' && trim($buffer) === '' && static::is_whitespace($string[$i + 1])) { - $tokens[] = (object)['type' => 'css-string', 'value' => $string[$i]]; - $buffer = ''; - break; - } + $tokens[] = (object)['type' => 'css-string', 'value' => $string[$i]]; + $buffer = ''; + break; + } - $buffer .= $string[$i]; - break; + $buffer .= $string[$i]; + break; - case '/': + case '/': - if ($i < $j && $string[$i + 1] == '*') { + if ($i < $j && $string[$i + 1] == '*') { - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); + $tokens[] = static::getType($buffer, $preserve_quotes); } - $params = static::match_comment($string, $i, $j); + $params = static::match_comment($string, $i, $j); $token = new stdClass; if ($params !== false) { $token->type = 'Comment'; - $token->value = $params; + $token->value = $params; - $tokens[] = $token; + $tokens[] = $token; - $i += strlen($params) - 1; + $i += strlen($params) - 1; } else { $token->type = 'invalid-comment'; - $token->value = implode('', array_slice($string, $i)); + $token->value = implode('', array_slice($string, $i)); - $tokens[] = $token; + $tokens[] = $token; - $i = $j; + $i = $j; } $buffer = ''; break; } - $token = static::getType($buffer, $preserve_quotes); + $token = static::getType($buffer, $preserve_quotes); - if (trim($buffer) === '') { + if (trim($buffer) === '') { - $tokens[] = (object)['type' => $context == 'css-function' ? 'operator' : 'separator', 'value' => '/']; - $buffer = ''; - break; - } + $tokens[] = (object)['type' => $context == 'css-function' ? 'operator' : 'separator', 'value' => '/']; + $buffer = ''; + break; + } - if (in_array($token->type, ['unit', 'number'])) { + if (in_array($token->type, ['unit', 'number'])) { - $tokens[] = $token; - $tokens[] = (object)['type' => $context == 'css-function' ? 'operator' : 'separator', 'value' => '/']; - $buffer = ''; - break; - } + $tokens[] = $token; + $tokens[] = (object)['type' => $context == 'css-function' ? 'operator' : 'separator', 'value' => '/']; + $buffer = ''; + break; + } - $buffer .= $string[$i]; - break; + $buffer .= $string[$i]; + break; - case '~': - case '^': - case '$': - case '=': + case '~': + case '^': + case '$': + case '=': - if (($string[$i] == '~' && $context === '') || - $context == 'attribute' && ($string[$i] == '=' || (isset($string[$i + 1]) && $string[$i + 1] == '='))) { + if (($string[$i] == '~' && $context === '') || + $context == 'attribute' && ($string[$i] == '=' || (isset($string[$i + 1]) && $string[$i + 1] == '='))) { - if (trim($buffer) !== '') { + if (trim($buffer) !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $token = end($tokens); + $token = end($tokens); - if (($token->type ?? '') == 'whitespace') { + if (($token->type ?? '') == 'whitespace') { - array_pop($tokens); - } + array_pop($tokens); + } - $buffer = ''; + $buffer = ''; - if ($string[$i] == '=') { + if ($string[$i] == '=') { - $tokens[] = (object)['type' => $context === 'attribute' ? 'operator' : 'css-string', 'value' => '=']; - } else if ($context === 'attribute') { + $tokens[] = (object)['type' => $context === 'attribute' ? 'operator' : 'css-string', 'value' => '=']; + } else if ($context === 'attribute') { - $tokens[] = (object)['type' => 'operator', 'value' => $string[$i++] .$string[$i]]; - } else { + $tokens[] = (object)['type' => 'operator', 'value' => $string[$i++] . $string[$i]]; + } else { - $tokens[] = (object)['type' => 'css-string', 'value' => $string[$i]]; - } + $tokens[] = (object)['type' => 'css-string', 'value' => $string[$i]]; + } - break; - } + break; + } - $buffer .= $string[$i]; - break; + $buffer .= $string[$i]; + break; - case ',': - case ':': + case ',': + case ':': - if ($string[$i] == ':' && $context != 'css-parenthesis-expression') { + if ($string[$i] == ':' && $context != 'css-parenthesis-expression') { - $buffer .= $string[$i]; - continue 2; - } + $buffer .= $string[$i]; + continue 2; + } - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - $token = new stdClass; - $token->type = 'separator'; - $token->value = $string[$i]; - $tokens[] = $token; + $token = new stdClass; + $token->type = 'separator'; + $token->value = $string[$i]; + $tokens[] = $token; - $buffer = ''; - break; + $buffer = ''; + break; - default: + default: - if ($string[$i] == '!') { + if ($string[$i] == '!') { - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType(rtrim($buffer), $preserve_quotes); - $buffer = ''; - } - } + $tokens[] = static::getType(rtrim($buffer), $preserve_quotes); + $buffer = ''; + } + } - $buffer .= $string[$i]; - } - } + $buffer .= $string[$i]; + } + } - if ($buffer !== '') { + if ($buffer !== '') { - $tokens[] = static::getType($buffer, $preserve_quotes); - } + $tokens[] = static::getType($buffer, $preserve_quotes); + } - return $tokens; - } + return $tokens; + } - /** - * escape multibyte sequence - * @param string $value - * @return string - */ - public static function escape(string $value): string + /** + * escape multibyte sequence + * @param string $value + * @return string + */ + public static function escape(string $value): string { - $result = ''; + $result = ''; if (mb_detect_encoding($value) == 'ASCII') { return $value; } - foreach (mb_str_split($value) as $val) { + foreach (mb_str_split($value) as $val) { - if ($val == "\0") { + if ($val == "\0") { - $result .= '\FFFD'; - continue; - } + $result .= '\FFFD'; + continue; + } - $base = mb_ord($val); + $base = mb_ord($val); - if ($base > 128) { + if ($base > 128) { - $result .= '\\' . strtoupper(base_convert($base, 10, 16)); - } else { + $result .= '\\' . strtoupper(base_convert($base, 10, 16)); + } else { - $result .= $val; - } - } + $result .= $val; + } + } - return $result; - } + return $result; + } - /** - * @param array $array - * @param mixed $search - * @param int $offset - * @return false|int - */ - protected static function indexOf(array $array, string $search, int $offset = 0): bool|int + /** + * @param array $array + * @param mixed $search + * @param int $offset + * @return false|int + */ + protected static function indexOf(array $array, string $search, int $offset = 0): bool|int { - $length = count($array); + $length = count($array); - for ($i = $offset; $i < $length; $i++) { + for ($i = $offset; $i < $length; $i++) { - if ($array[$i] === $search) { + if ($array[$i] === $search) { - return $i; - } - } + return $i; + } + } - return false; - } + return false; + } - /** - * @param string $token - * @param bool $preserve_quotes - * @return stdClass - */ - protected static function getType(string $token, bool $preserve_quotes = false): stdClass + /** + * @param string $token + * @param bool $preserve_quotes + * @return stdClass + */ + protected static function getType(string $token, bool $preserve_quotes = false): stdClass { - $type = new stdClass; + $type = new stdClass; - $type->value = $token; + $type->value = $token; - if (!str_starts_with($token, '#') && is_numeric($token)) { + if (!str_starts_with($token, '#') && is_numeric($token)) { - $type->type = 'number'; - } else if ($token == 'currentcolor' || isset(Color::COLORS_NAMES[$token]) || preg_match('#^\#([a-f0-9]{8}|[a-f0-9]{6}|[a-f0-9]{4}|[a-f0-9]{3})$#i', $token)) { + $type->type = 'number'; + } else if ($token == 'currentcolor' || isset(Color::COLORS_NAMES[$token]) || preg_match('#^\#([a-f0-9]{8}|[a-f0-9]{6}|[a-f0-9]{4}|[a-f0-9]{3})$#i', $token)) { - $type->type = 'color'; - $type->colorType = $token == 'currentcolor' ? 'keyword' : 'hex'; - } else if (preg_match('#^((([+\-])?(?=\d*[.eE])([0-9]+\.?[0-9]*|\.[0-9]+)([eE]([+\-])?[0-9]+)?)|(\d+|(\d*\.\d+)))([a-zA-Z]+|%)$#', $token, $matches)) { + $type->type = 'color'; + $type->colorType = $token == 'currentcolor' ? 'keyword' : 'hex'; + } else if (preg_match('#^((([+\-])?(?=\d*[.eE])([0-9]+\.?[0-9]*|\.[0-9]+)([eE]([+\-])?[0-9]+)?)|(\d+|(\d*\.\d+)))([a-zA-Z]+|%)$#', $token, $matches)) { - $type->type = 'unit'; - $type->value = $matches[1]; - $type->unit = $matches[9]; - } else { + $type->type = 'unit'; + $type->value = $matches[1]; + $type->unit = $matches[9]; + } else { - $type->type = 'css-string'; + $type->type = 'css-string'; - self::parseString($type, $preserve_quotes); - } + self::parseString($type, $preserve_quotes); + } - return $type; - } + return $type; + } - /** - * convert to an object - * @return stdClass - */ - public function toObject(): stdClass + /** + * convert to an object + * @return stdClass + */ + public function toObject(): stdClass { - $result = new stdClass; + $result = new stdClass; - foreach ($this->data as $key => $value) { + foreach ($this->data as $key => $value) { - $val = $value; + $val = $value; - if (!is_null($key) && $key !== "") { + if (!is_null($key) && $key !== "") { - $result->{$key} = $val; - } - } + $result->{$key} = $val; + } + } - return $result; - } + return $result; + } - /** - * return the list of keywords - * @return array - * @ignore - */ - public static function keywords(): array - { + /** + * return the list of keywords + * @return array + * @ignore + */ + public static function keywords(): array + { - return static::$keywords; - } + return static::$keywords; + } - /** - * @param string $string - * @param array|null $keywords - * @return string|null - * @ignore - */ - public static function matchKeyword(string $string, array $keywords = null): ?string - { + /** + * @param string $string + * @param array|null $keywords + * @return string|null + * @ignore + */ + public static function matchKeyword(string $string, array $keywords = null): ?string + { - if (is_null($keywords)) { + if (is_null($keywords)) { - $keywords = static::keywords(); - } - $string = trim($string, ";\n\t\r "); - $string = static::stripQuotes($string, true); + $keywords = static::keywords(); + } + $string = trim($string, ";\n\t\r "); + $string = static::stripQuotes($string, true); - foreach ($keywords as $keyword) { + foreach ($keywords as $keyword) { - if (strcasecmp($string, $keyword) === 0) { + if (strcasecmp($string, $keyword) === 0) { - return $keyword; - } - } + return $keyword; + } + } - return null; - } + return null; + } /** * @param object|null $value @@ -1394,38 +1399,38 @@ public static function matchKeyword(string $string, array $keywords = null): ?st * @return string|null * @throws Exception */ - public static function getNumericValue(?object $value, array $options = []): ?string - { + public static function getNumericValue(?object $value, array $options = []): ?string + { - if (is_null($value) || $value->value === '') { + if (is_null($value) || $value->value === '') { - return null; - } + return null; + } - return Number::compress(($value->unit ?? '') == '%' ? $value->value / 100 : Value::renderTokens([$value], $options)); - } + return Number::compress(($value->unit ?? '') == '%' ? $value->value / 100 : Value::renderTokens([$value], $options)); + } - /** - * @param object $value - * @return string - */ - public static function getRGBValue(object $value): string - { + /** + * @param object $value + * @return string + */ + public static function getRGBValue(object $value): string + { - return Number::compress(($value->unit ?? '') == '%' ? 255 * $value->value / 100 : $value->value); - } + return Number::compress(($value->unit ?? '') == '%' ? 255 * $value->value / 100 : $value->value); + } - /** - * @param object|null $value + /** + * @param object|null $value * @return string|null - */ - public static function getAngleValue(?object $value): ?string - { + */ + public static function getAngleValue(?object $value): ?string + { - if (is_null($value) || $value->value === '') { + if (is_null($value) || $value->value === '') { - return null; - } + return null; + } return match ($value->unit ?? '') { 'rad' => floatval((string)$value->value) / (2 * pi()), @@ -1436,17 +1441,8 @@ public static function getAngleValue(?object $value): ?string } - /** - * convert to string - * @return string - */ - public function __toString() - { - return $this->render(); - } - - public function jsonSerialize() - { - return $this->render(); - } + public function jsonSerialize() + { + return (string)$this; + } } \ No newline at end of file diff --git a/src/Value/Background.php b/src/Value/Background.php index fa909d83..65e0228b 100755 --- a/src/Value/Background.php +++ b/src/Value/Background.php @@ -42,8 +42,8 @@ class Background extends ShortHand /** * @inheritDoc */ - public static function matchPattern(array $tokens) - { + public static function matchPattern(array $tokens): array + { $tokens = static::reduce(parent::matchPattern($tokens)); diff --git a/src/Value/BackgroundImage.php b/src/Value/BackgroundImage.php index 11fbb946..4e9661ff 100755 --- a/src/Value/BackgroundImage.php +++ b/src/Value/BackgroundImage.php @@ -29,12 +29,7 @@ protected static function validate(stdClass $data): bool { return isset($data->name) && isset($data->arguments) && is_array($data->arguments); } - public function render(array $options = []): string - { - return $this->data->value ?? parent::render($options); - } - - public static function doRender(object $data, array $options = []) + public static function doRender(object $data, array $options = []): string { return $data->value ?? parent::doRender($data, $options); } diff --git a/src/Value/BackgroundPosition.php b/src/Value/BackgroundPosition.php index d4f49485..353bec45 100755 --- a/src/Value/BackgroundPosition.php +++ b/src/Value/BackgroundPosition.php @@ -198,13 +198,7 @@ protected static function check(array $set, $value, ...$values): bool return true; } - public function render(array $options = []): string - { - - return static::doRender($this->data, $options); - } - - public static function doRender(object $data, array $options = []) { + public static function doRender(object $data, array $options = []): string { if (isset($data->unit)) { diff --git a/src/Value/BackgroundRepeat.php b/src/Value/BackgroundRepeat.php index 84192c06..c1c183a2 100755 --- a/src/Value/BackgroundRepeat.php +++ b/src/Value/BackgroundRepeat.php @@ -3,7 +3,7 @@ namespace TBela\CSS\Value; use Exception; -use TBela\CSS\Property\Config; +use TBela\CSS\Config; use TBela\CSS\Value; /** @@ -118,4 +118,10 @@ protected static function doParse($string, $capture_whitespace = true, string $c return static::reduce($tokens); } + + public static function doRender(object $data, array $options = []): string + { + var_dump($data); + throw new \RuntimeException('Not implemented', 501); + } } diff --git a/src/Value/Color.php b/src/Value/Color.php index 315c2e5a..f6b21905 100755 --- a/src/Value/Color.php +++ b/src/Value/Color.php @@ -37,20 +37,10 @@ public static function match(object $data, $type): bool return $type == 'color'; } - /** - * @inheritDoc - * @throws \Exception - */ - public function render(array $options = []): string - { - - return static::doRender($this->data, $options); - } - /** * @throws \Exception */ - public static function doRender(object $data, array $options = []) { + public static function doRender(object $data, array $options = []): string { if (!isset($data->rgba)) { diff --git a/src/Value/CssAttribute.php b/src/Value/CssAttribute.php index 092ae8f7..0492c15f 100755 --- a/src/Value/CssAttribute.php +++ b/src/Value/CssAttribute.php @@ -16,12 +16,7 @@ protected static function validate(stdClass $data): bool { return isset($data->arguments) && is_array($data->arguments); } - public function render(array $options = []): string { - - return '['. $this->data->arguments->render($options).']'; - } - - public static function doRender(object $data, array $options = []) { + public static function doRender(object $data, array $options = []): string { return '['. Value::renderTokens($data->arguments, $options).']'; } diff --git a/src/Value/CssFunction.php b/src/Value/CssFunction.php index eec09682..47adbb7a 100755 --- a/src/Value/CssFunction.php +++ b/src/Value/CssFunction.php @@ -22,15 +22,7 @@ protected static function validate(stdClass $data): bool { /** * @inheritDoc */ - public function render(array $options = []): string { - - return $this->data->name.'('. $this->data->arguments->render($options).')'; - } - - /** - * @inheritDoc - */ - public static function doRender(object $data, array $options = []) + public static function doRender(object $data, array $options = []): string { return $data->name.'('. Value::renderTokens($data->arguments, $options).')'; } diff --git a/src/Value/CssString.php b/src/Value/CssString.php index a949985a..c3c7fc85 100755 --- a/src/Value/CssString.php +++ b/src/Value/CssString.php @@ -37,17 +37,7 @@ protected function __construct($data) parent::__construct($data); } - /** - * @inheritDoc - * @ignore - */ - public function render(array $options = []): string - { - - return static::doRender($this->data, $options); - } - - public static function doRender(object $data, array $options = []) { + public static function doRender(object $data, array $options = []): string { $q = $data->q ?? ''; diff --git a/src/Value/CssUrl.php b/src/Value/CssUrl.php index 82e95179..57a29f9d 100755 --- a/src/Value/CssUrl.php +++ b/src/Value/CssUrl.php @@ -16,15 +16,10 @@ protected static function validate(stdClass $data): bool { return $data->name ?? null === 'url' && isset($data->arguments) && is_array($data->arguments); } - public function render(array $options = []): string { - - return $this->data->name.'('. preg_replace('~^(["\'])([^\s\\1]+)\\1$~', '$2', $this->data->arguments->render($options)).')'; - } - /** * @inheritDoc */ - public static function doRender(object $data, array $options = []) + public static function doRender(object $data, array $options = []): string { return $data->name.'('. preg_replace('~^(["\'])([^\s\\1]+)\\1$~', '$2', Value::renderTokens($data->arguments, $options)).')'; } diff --git a/src/Value/FontSize.php b/src/Value/FontSize.php index 18d5b5c5..9556f531 100755 --- a/src/Value/FontSize.php +++ b/src/Value/FontSize.php @@ -45,13 +45,4 @@ public static function matchToken(object $token, object $previousToken = null, o return $token->type == static::type(); } - - /** - * @inheritDoc - */ - public function render(array $options = []): string - { - - return static::doRender($this->data, $options); - } } diff --git a/src/Value/FontStretch.php b/src/Value/FontStretch.php index 9d1fe63a..876f906d 100755 --- a/src/Value/FontStretch.php +++ b/src/Value/FontStretch.php @@ -33,12 +33,12 @@ class FontStretch extends Value /** * @inheritDoc */ - public function render(array $options = []): string + public static function dRender($data, array $options = []): string { if (!empty($options['compress'])) { - $value = $this->data->value; + $value = $data->value; if (isset(static::$keywords[$value])) { @@ -46,7 +46,7 @@ public function render(array $options = []): string } } - return $this->data->value; + return $data->value; } /** diff --git a/src/Value/FontWeight.php b/src/Value/FontWeight.php index c91d5cae..09d39a3c 100755 --- a/src/Value/FontWeight.php +++ b/src/Value/FontWeight.php @@ -11,140 +11,131 @@ */ class FontWeight extends Value { - protected static array $keywords = [ - 'thin' => '100', - 'hairline' => '100', - 'extra light' => '200', - 'ultra light' => '200', - 'light' => '300', - 'normal' => '400', - 'regular' => '400', - 'medium' => '500', - 'semi bold' => '600', - 'demi bold' => '600', - 'bold' => '700', - 'extra bold' => '800', - 'ultra bold' => '800', - 'black' => '900', - 'heavy' => '900', - 'extra black' => '950', - 'ultra black' => '950', - 'lighter' => 'lighter', - 'bolder' => 'bolder' - ]; + protected static array $keywords = [ + 'thin' => '100', + 'hairline' => '100', + 'extra light' => '200', + 'ultra light' => '200', + 'light' => '300', + 'normal' => '400', + 'regular' => '400', + 'medium' => '500', + 'semi bold' => '600', + 'demi bold' => '600', + 'bold' => '700', + 'extra bold' => '800', + 'ultra bold' => '800', + 'black' => '900', + 'heavy' => '900', + 'extra black' => '950', + 'ultra black' => '950', + 'lighter' => 'lighter', + 'bolder' => 'bolder' + ]; - protected static array $defaults = ['normal', '400', 'regular']; + protected static array $defaults = ['normal', '400', 'regular']; - /** - * @inheritDoc - */ - public function render(array $options = []): string - { + public static function doRender(object $data, array $options = []): string + { - return static::doRender($this->data, $options); - } + $value = static::matchKeyword($data->value); - public static function doRender(object $data, array $options = []) - { + if (!empty($options['compress'])) { - $value = static::matchKeyword($data->value); + if (isset(static::$keywords[$value])) { - if (!empty($options['compress'])) { + $value = static::$keywords[$value]; + } - if (isset(static::$keywords[$value])) { + if (is_numeric($value)) { - $value = static::$keywords[$value]; - } + return Number::compress($value); + } + } - if (is_numeric($value)) { + if (array_key_exists($value, static::$keywords) && str_contains($value, ' ')) { - return Number::compress($value); - } - } + return '"' . $value . '"'; + } - if (array_key_exists($value, static::$keywords) && str_contains($value, ' ')) { + return $data->value; + } - return '"' . $value . '"'; - } + /** + * @inheritDoc + */ + public static function match(object $data, $type): bool + { - return $data->value; - } + return $type == 'font-weight'; + } - /** - * @inheritDoc - */ - public static function match(object $data, $type): bool - { + /** + * @inheritDoc + */ + public static function matchToken(object $token, object $previousToken = null, object $previousValue = null, object $nextToken = null, object $nextValue = null, int $index = null, array $tokens = []): bool + { - return $type == 'font-weight'; - } + if ($token->type == 'number' && $token->value > 0 && $token->value <= 1000) { - /** - * @inheritDoc - */ - public static function matchToken(object $token, object $previousToken = null, object $previousValue = null, object $nextToken = null, object $nextValue = null, int $index = null, array $tokens = []): bool - { + return true; + } - if ($token->type == 'number' && $token->value > 0 && $token->value <= 1000) { + if (isset($token->value)) { - return true; - } + $matchKeyWord = static::matchKeyword($token->value); - if (isset($token->value)) { + if (!is_null($matchKeyWord)) { - $matchKeyWord = static::matchKeyword($token->value); + return true; + } + } - if (!is_null($matchKeyWord)) { + return $token->type == static::type(); + } - return true; - } - } + /** + * @inheritDoc + * @throws Exception + */ + protected static function doParse($string, $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): array + { - return $token->type == static::type(); - } + $type = static::type(); - /** - * @inheritDoc - * @throws Exception - */ - protected static function doParse($string, $capture_whitespace = true, string $context = '', bool $preserve_quotes = false): array - { + $matchKeyword = static::matchKeyword($string); - $type = static::type(); + if (!is_null($matchKeyword)) { - $matchKeyword = static::matchKeyword($string); + return [(object)['type' => $type, 'value' => $matchKeyword]]; + } - if (!is_null($matchKeyword)) { + $tokens = static::getTokens($string, $capture_whitespace, $context); - return [(object)['type' => $type, 'value' => $matchKeyword]]; - } + foreach ($tokens as $token) { - $tokens = static::getTokens($string, $capture_whitespace, $context); + if (static::matchToken($token)) { - foreach ($tokens as $token) { + if ($token->type == 'css-string') { - if (static::matchToken($token)) { + $value = static::matchKeyword($token->value); - if ($token->type == 'css-string') { + if (!is_null($value)) { - $value = static::matchKeyword($token->value); + $token->value = $value; + } + } - if (!is_null($value)) { + $token->type = $type; + } + } - $token->value = $value; - } - } + return static::reduce($tokens); + } - $token->type = $type; - } - } + public static function keywords(): array + { - return static::reduce($tokens); - } - - public static function keywords(): array - { - - return array_keys(static::$keywords); - } + return array_keys(static::$keywords); + } } diff --git a/src/Value/InvalidCssFunction.php b/src/Value/InvalidCssFunction.php index 49e26b58..b9c2cf7f 100755 --- a/src/Value/InvalidCssFunction.php +++ b/src/Value/InvalidCssFunction.php @@ -20,12 +20,13 @@ protected static function validate(stdClass $data): bool { return isset($data->name) && isset($data->arguments) && is_array($data->arguments); } - /** - * @inheritDoc - */ - public function render(array $options = []): string { + /** + * + * @throws \Exception + */ + public static function doRender($data, array $options = []): string { - return $this->data->name.'('. $this->data->arguments->render($options); + return $data->name.'('. Value::renderTokens($data->arguments, $options).')'; } /** diff --git a/src/Value/InvalidCssString.php b/src/Value/InvalidCssString.php index befd0b14..3c65b41e 100755 --- a/src/Value/InvalidCssString.php +++ b/src/Value/InvalidCssString.php @@ -30,13 +30,13 @@ public function getValue() { } /** - * @inheritDoc - * @ignore + * + * @ignore */ - public function render(array $options = []): string + public static function doRender(object $data, array $options = []): string { - return $this->data->q.$this->data->value; + return $data->q.$data->value; } public static function doRecover(object $data):object { diff --git a/src/Value/LineHeight.php b/src/Value/LineHeight.php index 81b65706..c1c1c279 100755 --- a/src/Value/LineHeight.php +++ b/src/Value/LineHeight.php @@ -54,14 +54,6 @@ public static function matchToken(object $token, object $previousToken = null, o return $token->type == static::type(); } - /** - * @inheritDoc - */ - public function render(array $options = []): string - { - return static::doRender($this->data, $options); - } - public static function doRender(object $data, array $options = []): string { $value = $data->value; diff --git a/src/Value/Number.php b/src/Value/Number.php index cb31b9fc..a4b2c6bb 100755 --- a/src/Value/Number.php +++ b/src/Value/Number.php @@ -96,15 +96,8 @@ public static function compress(string $value, array $options = []): string return implode('.', $value); } - /** - * @inheritDoc - */ - public function render(array $options = []): string - { - return static::doRender($this->data, $options); - } - public static function doRender(object $data, array $options = []) + public static function doRender(object $data, array $options = []): string { diff --git a/src/Value/ShortHand.php b/src/Value/ShortHand.php index 29bbe2da..8031aff7 100755 --- a/src/Value/ShortHand.php +++ b/src/Value/ShortHand.php @@ -3,8 +3,8 @@ namespace TBela\CSS\Value; use Exception; -use TBela\CSS\Property\Config; -use \TBela\CSS\Value; +use TBela\CSS\Config; +use TBela\CSS\Value; /** * parse shorthand @@ -202,4 +202,10 @@ public static function matchPattern(array $tokens) return $tokens; } + + public static function doRender(object $data, array $options = []): string + { + var_dump($data); + throw new \RuntimeException('Not implemented', 501); + } } diff --git a/src/Value/Unit.php b/src/Value/Unit.php index a5607b19..b2332557 100755 --- a/src/Value/Unit.php +++ b/src/Value/Unit.php @@ -28,16 +28,7 @@ public static function match (object $data, $type): bool { return $dataType == static::type() || ($type == 'number' && $data->value == 0); } - /** - * @inheritDoc - */ - public function render(array $options = []): string - { - - return static::doRender($this->data, $options); - } - - public static function doRender(object $data, array $options = []) { + public static function doRender(object $data, array $options = []): string { /** * @see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types#quantities diff --git a/src/Value/UnitTrait.php b/src/Value/UnitTrait.php index 18a14d99..18a3188c 100755 --- a/src/Value/UnitTrait.php +++ b/src/Value/UnitTrait.php @@ -12,29 +12,29 @@ trait UnitTrait /** * @inheritDoc */ - - public function render(array $options = []): string - { - - if (isset($this->data->unit)) { - - if ($this->data->value == '0') { - - return '0'; - } - - if (!empty($options['compress'])) { - - return Number::compress($this->data->value) . $this->data->unit; - } - - return $this->data->value . $this->data->unit; - } - - return $this->data->value; - } - - public static function doRender(object $data, array $options = []) { +// +// public function render(array $options = []): string +// { +// +// if (isset($this->data->unit)) { +// +// if ($this->data->value == '0') { +// +// return '0'; +// } +// +// if (!empty($options['compress'])) { +// +// return Number::compress($this->data->value) . $this->data->unit; +// } +// +// return $this->data->value . $this->data->unit; +// } +// +// return $this->data->value; +// } + + public static function doRender(object $data, array $options = []): string { $value = $data->value; diff --git a/src/Value/ValueTrait.php b/src/Value/ValueTrait.php index 71980351..d8147b9e 100755 --- a/src/Value/ValueTrait.php +++ b/src/Value/ValueTrait.php @@ -3,7 +3,7 @@ namespace TBela\CSS\Value; // pattern font-style font-variant font-weight font-stretch font-size / line-height <'font-family'> -use TBela\CSS\Property\Config; +use TBela\CSS\Config; /** * parse font diff --git a/src/Value/Whitespace.php b/src/Value/Whitespace.php index 002ca302..177734ef 100755 --- a/src/Value/Whitespace.php +++ b/src/Value/Whitespace.php @@ -27,10 +27,7 @@ public function getValue () { return ' '; } - /** - * @inheritDoc - */ - public function render(array $options = []): string + public static function doRender(object $data, array $options = []): string { return ' '; } diff --git a/test/sourcemap/generated/nested.test.css.map b/test/sourcemap/generated/nested.test.css.map index 9ec0d310..4a5d7a09 100755 --- a/test/sourcemap/generated/nested.test.css.map +++ b/test/sourcemap/generated/nested.test.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/nested\/nested.css"],"names":[],"mappings":";AACA;;;;;;AAOI;;;AAEE;;;;AAKF;;;;;;AAME;;;;;AASN;;;AAEA;;;AAEA;;;;AAIA"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/nested\/nested.css"],"names":[],"mappings":";AACA;;;;;;AAOI;;;AAEE;;;;AAKF;;;;;;AAME;;;;;AASN;;;AAEA;;;AAEA;;;;AAIA"} \ No newline at end of file diff --git a/test/sourcemap/generated/nested.test.min.css.map b/test/sourcemap/generated/nested.test.min.css.map index 0366020f..905364fc 100755 --- a/test/sourcemap/generated/nested.test.min.css.map +++ b/test/sourcemap/generated/nested.test.min.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/nested\/nested.css"],"names":[],"mappings":"AACA,sEAOI,sCAEE,+DAKF,kFAME,6EASN,iBAEA,gBAEA,qBAIA"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/nested\/nested.css"],"names":[],"mappings":"AACA,sEAOI,sCAEE,+DAKF,kFAME,6EASN,iBAEA,gBAEA,qBAIA"} \ No newline at end of file diff --git a/test/sourcemap/generated/sourcemap.generated.import.test.css.map b/test/sourcemap/generated/sourcemap.generated.import.test.css.map index cebc7cc2..a9643dbd 100755 --- a/test/sourcemap/generated/sourcemap.generated.import.test.css.map +++ b/test/sourcemap/generated/sourcemap.generated.import.test.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":";AACA;;;;;;;;;AAaA;;;;;;;;;;;;;;;AAgBA;;;;AC7BA;;;AAIA;;;;;ACFI"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":";AACA;;;;;;;;;AAaA;;;;;;;;;;;;;;;AAgBA;;;;AC7BA;;;AAIA;;;;;ACFI"} \ No newline at end of file diff --git a/test/sourcemap/generated/sourcemap.generated.import.test.min.css.map b/test/sourcemap/generated/sourcemap.generated.import.test.min.css.map index 3a2a56fd..b5847d61 100755 --- a/test/sourcemap/generated/sourcemap.generated.import.test.min.css.map +++ b/test/sourcemap/generated/sourcemap.generated.import.test.min.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA,6DAaA,+LAgBA,6DC7BA,+BAIA,8CCFI"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA,6DAaA,+LAgBA,6DC7BA,+BAIA,8CCFI"} \ No newline at end of file diff --git a/test/sourcemap/generated/sourcemap.generated.test.css.map b/test/sourcemap/generated/sourcemap.generated.test.css.map index 2f356e85..8b639aac 100755 --- a/test/sourcemap/generated/sourcemap.generated.test.css.map +++ b/test/sourcemap/generated/sourcemap.generated.test.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA;;;;;;;;;AAaA;;;;;;;;;;;;;;;AAgBA;;;AC7BA;;;AAIA;;;;ACFI"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA;;;;;;;;;AAaA;;;;;;;;;;;;;;;AAgBA;;;AC7BA;;;AAIA;;;;ACFI"} \ No newline at end of file diff --git a/test/sourcemap/generated/sourcemap.generated.test.min.css.map b/test/sourcemap/generated/sourcemap.generated.test.min.css.map index 3a2a56fd..b5847d61 100755 --- a/test/sourcemap/generated/sourcemap.generated.test.min.css.map +++ b/test/sourcemap/generated/sourcemap.generated.test.min.css.map @@ -1 +1 @@ -{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/import\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA,6DAaA,+LAgBA,6DC7BA,+BAIA,8CCFI"} \ No newline at end of file +{"version":3,"file":"","sourceRoot":"","sources":["\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.2.css","\/home\/tbela\/git\/css\/test\/sourcemap\/sourcemap.media.css"],"names":[],"mappings":"AACA,6DAaA,+LAgBA,6DC7BA,+BAIA,8CCFI"} \ No newline at end of file diff --git a/test/src/AstTest.php b/test/src/AstTest.php index 9b562550..e1f8e68f 100755 --- a/test/src/AstTest.php +++ b/test/src/AstTest.php @@ -56,7 +56,8 @@ public function identifierProvider(): array } '); - $data[] = [(string) $parser, (string) $parser->parse()]; + $data[] = [(string) $parser, + (string) $parser->parse()]; $parser->setContent(' diff --git a/test/src/BackgroundTest.php b/test/src/BackgroundTest.php index 0a65d179..154f8d69 100755 --- a/test/src/BackgroundTest.php +++ b/test/src/BackgroundTest.php @@ -2,8 +2,8 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; +use TBela\CSS\Element\Declaration\ValueList; use TBela\CSS\Parser; -use TBela\CSS\Property\PropertyList; use TBela\CSS\Value; final class BackgroundTest extends TestCase @@ -318,7 +318,6 @@ public function backgroundProvider() { Value::renderTokens(Value::parse('transparent !important ', 'background', true, '', '', true))]; /* - * , ], ['background-size', 'cover, contain'], ['background-image', 'url("../../media/examples/lizard.png"), url("../../media/examples/firefox-logo.svg")'], ['background-repeat', 'repeat, repeat'], @@ -328,55 +327,55 @@ public function backgroundProvider() { ['background-image', 'none, none'], ['background-size', 'auto, auto'], */ - $property = new PropertyList(); + $property = new ValueList(); - $property->set('background', 'center / contain no-repeat url("../../media/examples/firefox-logo.svg"), + $property->set('background', 'center / contain no-repeat url(../../media/examples/firefox-logo.svg), #eee 17% url("../../media/examples/lizard.png") '); $data[] = [ - 'background: center/contain no-repeat url("../../media/examples/firefox-logo.svg"), #eee 17% url("../../media/examples/lizard.png")', + 'background: center/contain no-repeat url(../../media/examples/firefox-logo.svg), #eee 17% url(../../media/examples/lizard.png)', (string) $property ]; $property->set('background-size', 'cover, contain' ); $data[] = [ - 'background: url("../../media/examples/firefox-logo.svg") center/cover no-repeat, url("../../media/examples/lizard.png") #eee 17%/contain', + 'background: url(../../media/examples/firefox-logo.svg) center/cover no-repeat, url(../../media/examples/lizard.png) #eee 17%/contain', (string) $property ]; $property->set('background-image', ' url("../../media/examples/lizard.png"), url("../../media/examples/firefox-logo.svg")' ); $data[] = [ - 'background: url("../../media/examples/lizard.png") center/cover no-repeat, url("../../media/examples/firefox-logo.svg") #eee 17%/contain', + 'background: url(../../media/examples/lizard.png) center/cover no-repeat, url(../../media/examples/firefox-logo.svg) #eee 17%/contain', (string) $property ]; $property->set('background-repeat', ' repeat, repeat' ); $data[] = [ - 'background: url("../../media/examples/lizard.png") center/cover, url("../../media/examples/firefox-logo.svg") #eee 17%/contain', + 'background: url(../../media/examples/lizard.png) center/cover, url(../../media/examples/firefox-logo.svg) #eee 17%/contain', (string) $property ]; $property->set('background-size', 'auto, auto' ); $data[] = [ - 'background: url("../../media/examples/lizard.png") center, url("../../media/examples/firefox-logo.svg") #eee 17%', + 'background: url(../../media/examples/lizard.png) center, url(../../media/examples/firefox-logo.svg) #eee 17%', (string) $property ]; $property->set('background-color', 'blue, red' ); $data[] = [ - 'background: url("../../media/examples/lizard.png") blue center, url("../../media/examples/firefox-logo.svg") red 17%', + 'background: url(../../media/examples/lizard.png) blue center, url(../../media/examples/firefox-logo.svg) red 17%', (string) $property ]; $property->set('background-size', 'auto 10%, 25% auto' ); $data[] = [ - 'background: url("../../media/examples/lizard.png") blue center/auto 10%, url("../../media/examples/firefox-logo.svg") red 17%/25%', + 'background: url(../../media/examples/lizard.png) blue center/auto 10%, url(../../media/examples/firefox-logo.svg) red 17%/25%', (string) $property ]; @@ -401,7 +400,7 @@ public function backgroundComputeProvider() { $data = []; - $property = new PropertyList(); + $property = new ValueList(); $property->set('background', 'none'); $data[] = [ diff --git a/test/src/ElementPropertiesTest.php b/test/src/ElementPropertiesTest.php index 384aaae3..369a2c44 100755 --- a/test/src/ElementPropertiesTest.php +++ b/test/src/ElementPropertiesTest.php @@ -104,9 +104,7 @@ public function elementPropertiesProvider () { margin-left: 3px !important; }'), '.btnflexanimate:hover { - margin-top: 2px!important; - margin-right: 2px!important; - margin-bottom: 2px!important; + margin: 2px!important; margin-left: 3px!important }' ]; diff --git a/test/src/FontTest.php b/test/src/FontTest.php index 88e42efe..9fcc77a4 100755 --- a/test/src/FontTest.php +++ b/test/src/FontTest.php @@ -2,10 +2,9 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; -use TBela\CSS\Element; use TBela\CSS\Compiler; +use TBela\CSS\Element\Declaration\ValueList; use TBela\CSS\Parser; -use TBela\CSS\Property\PropertyList; final class FontTest extends TestCase { @@ -70,7 +69,7 @@ public function fontComputeProvider () { $data = []; - $propertyList = new PropertyList(); + $propertyList = new ValueList(); $propertyList->set('font', 'italic 1.2em "Fira Sans", serif'); diff --git a/test/src/PropertiesTest.php b/test/src/PropertiesTest.php index bb689141..853c9c4a 100755 --- a/test/src/PropertiesTest.php +++ b/test/src/PropertiesTest.php @@ -2,21 +2,21 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; -use TBela\CSS\Property\PropertyList; +use TBela\CSS\Element\Declaration\ValueList; require_once __DIR__.'/../bootstrap.php'; final class PropertiesTest extends TestCase { /** - * @param PropertyList $propertylist + * @param ValueList $propertylist * @param $name * @param $value * @param $expected * @return void * @dataProvider PropertyComposeProvider */ - public function testPropertyCompose(PropertyList $propertylist, $name, $value, $expected) + public function testPropertyCompose(ValueList $propertylist, $name, $value, $expected) { $propertylist->set($name, $value); @@ -24,13 +24,13 @@ public function testPropertyCompose(PropertyList $propertylist, $name, $value, $ } /** - * @param PropertyList $property + * @param ValueList $property * @param $name * @param $value * @param $expected * @dataProvider propertySetProvider */ - public function testPropertySet(PropertyList $property, $name, $value, $expected) + public function testPropertySet(ValueList $property, $name, $value, $expected) { $property->set($name, $value); @@ -42,13 +42,13 @@ public function testPropertySet(PropertyList $property, $name, $value, $expected } /** - * @param PropertyList $property + * @param ValueList $property * @param $name * @param $value * @param $expected * @dataProvider propertyHackProvider */ - public function testPropertyHack(PropertyList $property, $name, $value, $expected) + public function testPropertyHack(ValueList $property, $name, $value, $expected) { $property->set($name, $value); @@ -62,7 +62,7 @@ public function testPropertyHack(PropertyList $property, $name, $value, $expecte public function propertyComposeProvider() { - $propertylist = new PropertyList(); + $propertylist = new ValueList(); return [ [$propertylist, 'border-top-left-radius', '1em 5em', 'border-top-left-radius: 1em 5em'], [$propertylist, 'border-top-right-radius', '1em 5em', 'border-top-left-radius: 1em 5em;' . "\n" . @@ -78,7 +78,7 @@ public function propertyComposeProvider() public function propertySetProvider() { - $property = new PropertyList(); + $property = new ValueList(); $data[] = [$property, 'border-radius', '10% 17% 10% 17% / 50% 20% 50% 20%', 'border-radius: 10% 17%/50% 20%']; $data[] = [$property, 'border-top-left-radius', '1em 5em', 'border-radius: 1em 17% 10%/5em 20% 50%']; @@ -86,7 +86,7 @@ public function propertySetProvider() $data[] = [$property, 'border-bottom-left-radius', '1em 5em', 'border-radius: 1em 1em 10%/5em 5em 50%']; $data[] = [$property, 'border-bottom-right-radius', '1em 5em', 'border-radius: 1em/5em']; - $property2 = new PropertyList(); + $property2 = new ValueList(); $data[] = [$property2, '-moz-border-radius', '10% 17% 10% 17% / 50% 20% 50% 20%', '-moz-border-radius: 10% 17%/50% 20%']; $data[] = [$property2, '-moz-border-radius-topleft', '1em 5em', '-moz-border-radius: 1em 17% 10%/5em 20% 50%']; @@ -94,7 +94,7 @@ public function propertySetProvider() $data[] = [$property2, '-moz-border-radius-bottomleft', '1em 5em', '-moz-border-radius: 1em 1em 10%/5em 5em 50%']; $data[] = [$property2, '-moz-border-radius-bottomright', '1em 5em', '-moz-border-radius: 1em/5em']; - $property3 = new PropertyList(); + $property3 = new ValueList(); $data[] = [$property3, '-webkit-border-radius', '10% 17% 10% 17% / 50% 20% 50% 20%', '-webkit-border-radius: 10% 17%/50% 20%']; $data[] = [$property3, '-webkit-border-top-left-radius', '1em 5em', '-webkit-border-radius: 1em 17% 10%/5em 20% 50%']; @@ -102,7 +102,7 @@ public function propertySetProvider() $data[] = [$property3, '-webkit-border-bottom-left-radius', '1em 5em', '-webkit-border-radius: 1em 1em 10%/5em 5em 50%']; $data[] = [$property3, '-webkit-border-bottom-right-radius', '1em 5em', '-webkit-border-radius: 1em/5em']; - $property1 = new PropertyList(); + $property1 = new ValueList(); $data[] = [$property1, 'margin', '5px 3px', 'margin: 5px 3px']; $data[] = [$property1, 'margin', '0 0 15px 15px', 'margin: 0 0 15px 15px']; @@ -114,7 +114,7 @@ public function propertySetProvider() $data[] = [$property1, 'margin-left', '0', 'margin: 15px 0']; $data[] = [$property1, 'margin', '0 auto', 'margin: 0 auto']; - $property4 = new PropertyList(); + $property4 = new ValueList(); $data[] = [$property4, 'margin-top', '5px \\9', 'margin-top: 5px \\9']; $data[] = [$property4, 'margin-left', '5px \\9', "margin-top: 5px \\9;\nmargin-left: 5px \\9"]; @@ -127,7 +127,7 @@ public function propertySetProvider() public function propertyHackProvider() { - $property = new PropertyList(); + $property = new ValueList(); $data[] = [$property, 'margin-top', '1px \\9', 'margin-top: 1px \\9']; $data[] = [$property, 'margin-right', '1px \\9', 'margin-top: 1px \\9;'."\n".'margin-right: 1px \\9']; diff --git a/test/src/QueryTest.php b/test/src/QueryTest.php index 34eff789..c38a8b4a 100755 --- a/test/src/QueryTest.php +++ b/test/src/QueryTest.php @@ -2,9 +2,8 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; -use TBela\CSS\Element; -use TBela\CSS\Compiler; use TBela\CSS\Parser; +use TBela\CSS\Renderer; require_once __DIR__.'/../bootstrap.php'; @@ -122,7 +121,12 @@ public function testQueryByClassName(array $expected, array $actual): void ); } - public function queryByClassNameProvider() { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function queryByClassNameProvider(): array + { $data = []; @@ -392,8 +396,11 @@ public function queryByClassNameProvider() { /* */ - public function dedupProvider () - { + /** + * @throws Parser\SyntaxError + */ + public function dedupProvider (): array + { $data = []; @@ -412,7 +419,12 @@ public function dedupProvider () /* */ - public function queryProvider () { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function queryProvider (): array + { $data = []; @@ -860,7 +872,12 @@ public function queryProvider () { return $data; } - public function querySelectorProvider () { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function querySelectorProvider (): array + { $data = []; @@ -955,7 +972,7 @@ public function querySelectorProvider () { ], array_map(function ($node) { - return (new \TBela\CSS\Renderer())->render($node, null, false); + return (new Renderer())->render($node); }, $element->query($context))]; $data[] = [ @@ -991,11 +1008,16 @@ public function querySelectorProvider () { return $data; } - public function queryFunctionsProvider () { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function queryFunctionsProvider (): array + { $data = []; - $css = '@font-face { + $css = '@font-face { font-family: "Bitstream Vera Serif Bold"; src: url("/static/styles/libs/font-awesome/fonts/fontawesome-webfont.fdf491ce5ff5.woff"); } @@ -1004,7 +1026,6 @@ public function queryFunctionsProvider () { color: rgb(255 0 0 / 1); } @media print { - } /** this is the story */ /** of the princess leia */ @@ -1015,19 +1036,15 @@ public function queryFunctionsProvider () { font-family: Arial, Helvetica, sans-serif; } strong { - } p { - } a { - color: white; } span { color: #343434; } - h1,h2, a { color: #fff; font-size: 50px; @@ -1035,6 +1052,7 @@ public function queryFunctionsProvider () { font-weight: bold; }'; + $parser = new Parser(); $parser->setContent($css); @@ -1073,7 +1091,7 @@ public function queryFunctionsProvider () { }', 1 => 'p:before { - content: print + content: "print" }' ], array_map('trim', $element->query($context))]; @@ -1172,7 +1190,7 @@ public function queryFunctionsProvider () { src: url(/static/styles/libs/font-awesome/fonts/fontawesome-webfont.fdf491ce5ff5.woff) }', 1 => 'p:before { - content: print; + content: "print"; color: red }', 2 => 'body { @@ -1215,8 +1233,12 @@ public function queryFunctionsProvider () { } /* */ - public function queryProviderOR () - { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function queryProviderOR (): array + { $data = []; @@ -1289,8 +1311,11 @@ public function queryProviderOR () } /* */ - public function renderQueryProvider () - { + /** + * @throws Parser\SyntaxError + */ + public function renderQueryProvider (): array + { $data = []; @@ -1375,8 +1400,12 @@ public function renderQueryProvider () } /* */ - public function combinatorProvider () - { + /** + * @throws Parser\SyntaxError + * @throws Exception + */ + public function combinatorProvider (): array + { $data = []; diff --git a/test/src/RendererTest.php b/test/src/RendererTest.php index c039ecdd..0e706363 100755 --- a/test/src/RendererTest.php +++ b/test/src/RendererTest.php @@ -6,11 +6,8 @@ use TBela\CSS\Compiler; use TBela\CSS\Element\AtRule; use TBela\CSS\Element\Declaration; -use TBela\CSS\Property\PropertyList; -use TBela\CSS\Renderer as RendererClass; use TBela\CSS\Interfaces\ObjectInterface; -use TBela\CSS\Value; -use TBela\CSS\Value\CSSFunction; +use TBela\CSS\Renderer as RendererClass; require_once __DIR__.'/../bootstrap.php'; @@ -80,7 +77,7 @@ public function provider () { } return $value; - }, $node->getRawValue())); + }, $node->getTokens())); } } diff --git a/test/src/VendorTest.php b/test/src/VendorTest.php index 043c209f..8c32386d 100755 --- a/test/src/VendorTest.php +++ b/test/src/VendorTest.php @@ -2,8 +2,8 @@ declare(strict_types=1); use PHPUnit\Framework\TestCase; +use TBela\CSS\Exceptions\IOException; use TBela\CSS\Parser; -use TBela\CSS\Renderer; require_once __DIR__.'/../bootstrap.php'; @@ -14,7 +14,7 @@ final class VendorTest extends TestCase * @param string $actual * @dataProvider vendorProvider */ - public function testVendor($expected, $actual): void + public function testVendor(string $expected, string $actual): void { $this->assertEquals( @@ -23,7 +23,11 @@ public function testVendor($expected, $actual): void ); } - public function vendorProvider() { + /** + * @throws IOException + */ + public function vendorProvider(): array + { $data = []; diff --git a/tool/BuildConfig.php b/tool/BuildConfig.php index c1d417d5..84ce3f77 100755 --- a/tool/BuildConfig.php +++ b/tool/BuildConfig.php @@ -10,7 +10,7 @@ require __DIR__ . '/../test/autoload.php'; // properties order is important! -use TBela\CSS\Property\Config; +use TBela\CSS\Config; $config = [ // shorthand that can be computed only when every shorthand property is defined because it will override properties that are not directly handled.