1<?php /** @noinspection PhpUnused */ 2 3/** 4* An ast visitor that compiles a css selector string 5* 6* @license http://www.opensource.org/licenses/mit-license.php The MIT License 7* @copyright Copyright 2010-2014 PhpCss Team 8*/ 9namespace PhpCss\Ast\Visitor { 10 11 use LogicException; 12 use PhpCss\Ast; 13 14 /** 15 * An ast visitor that compiles a css selector string 16 */ 17 class Css extends Overload { 18 19 private $_buffer = ''; 20 private $_inSelectorSequence = FALSE; 21 22 /** 23 * Clear the visitor object to visit another selector group 24 */ 25 public function clear(): void { 26 $this->_buffer = ''; 27 } 28 29 /** 30 * Return the collected selector string 31 */ 32 public function __toString() { 33 return $this->_buffer; 34 } 35 36 /** 37 * Validate the buffer before visiting a Ast\Selector\Group. 38 * If the buffer already contains data, throw an exception. 39 * 40 * @throws LogicException 41 * @param Ast\Selector\Group $group 42 * @return boolean 43 */ 44 public function visitEnterSelectorGroup(Ast\Selector\Group $group): bool { 45 if (!empty($this->_buffer)) { 46 throw new LogicException( 47 sprintf( 48 'Visitor buffer already contains data, can not visit "%s"', 49 get_class($group) 50 ) 51 ); 52 } 53 return TRUE; 54 } 55 56 /** 57 * If here is already data in the buffer, add a separator before starting the next. 58 * 59 * @return boolean 60 */ 61 public function visitEnterSelectorSequence(): bool { 62 if ($this->_inSelectorSequence) { 63 $this->_buffer .= ', '; 64 } 65 $this->_inSelectorSequence = TRUE; 66 return TRUE; 67 } 68 69 /** 70 * Output the universal selector to the buffer 71 * 72 * @param Ast\Selector\Simple\Universal $universal 73 * @return boolean 74 */ 75 public function visitSelectorSimpleUniversal(Ast\Selector\Simple\Universal $universal): bool { 76 if (!empty($universal->namespacePrefix) && $universal->namespacePrefix !== '*') { 77 $this->_buffer .= $universal->namespacePrefix.'|*'; 78 } else { 79 $this->_buffer .= '*'; 80 } 81 return TRUE; 82 } 83 84 /** 85 * Output the type selector to the buffer 86 * 87 * @param Ast\Selector\Simple\Type $type 88 * @return boolean 89 */ 90 public function visitSelectorSimpleType(Ast\Selector\Simple\Type $type): bool { 91 if (!empty($type->namespacePrefix) && $type->namespacePrefix !== '*') { 92 $this->_buffer .= $type->namespacePrefix.'|'.$type->elementName; 93 } else { 94 $this->_buffer .= $type->elementName; 95 } 96 return TRUE; 97 } 98 99 /** 100 * Output the class selector to the buffer 101 * 102 * @param Ast\Selector\Simple\Id $id 103 * @return boolean 104 */ 105 public function visitSelectorSimpleId(Ast\Selector\Simple\Id $id): bool { 106 $this->_buffer .= '#'.$id->id; 107 return TRUE; 108 } 109 110 /** 111 * Output the class selector to the buffer 112 * 113 * @param Ast\Selector\Simple\ClassName $class 114 * @return boolean 115 */ 116 public function visitSelectorSimpleClassName(Ast\Selector\Simple\ClassName $class): bool { 117 $this->_buffer .= '.'.$class->className; 118 return TRUE; 119 } 120 121 public function visitEnterSelectorCombinatorDescendant(): bool { 122 if ($this->_buffer !== '') { 123 $this->_buffer .= ' '; 124 } 125 $this->_inSelectorSequence = FALSE; 126 return TRUE; 127 } 128 129 public function visitEnterSelectorCombinatorChild(): bool { 130 $this->_buffer .= ($this->_buffer !== '') ? ' > ' : '> '; 131 $this->_inSelectorSequence = FALSE; 132 return TRUE; 133 } 134 135 public function visitEnterSelectorCombinatorFollower(): bool { 136 $this->_buffer .= ($this->_buffer !== '') ? ' ~ ' : '~ '; 137 $this->_inSelectorSequence = FALSE; 138 return TRUE; 139 } 140 141 public function visitEnterSelectorCombinatorNext(): bool { 142 $this->_buffer .= ($this->_buffer !== '') ? ' + ' : '+ '; 143 $this->_inSelectorSequence = FALSE; 144 return TRUE; 145 } 146 147 public function visitSelectorSimpleAttribute( 148 Ast\Selector\Simple\Attribute $attribute 149 ): bool { 150 $this->_buffer .= '['; 151 $this->_buffer .= $attribute->name; 152 if ($attribute->match !== Ast\Selector\Simple\Attribute::MATCH_EXISTS) { 153 $operatorStrings = [ 154 Ast\Selector\Simple\Attribute::MATCH_PREFIX => '^=', 155 Ast\Selector\Simple\Attribute::MATCH_SUFFIX => '$=', 156 Ast\Selector\Simple\Attribute::MATCH_SUBSTRING => '*=', 157 Ast\Selector\Simple\Attribute::MATCH_EQUALS => '=', 158 Ast\Selector\Simple\Attribute::MATCH_INCLUDES => '~=', 159 Ast\Selector\Simple\Attribute::MATCH_DASHMATCH => '|=' 160 ]; 161 $this->_buffer .= $operatorStrings[$attribute->match]; 162 $this->_buffer .= $this->quoteString($attribute->literal->value); 163 } 164 $this->_buffer .= ']'; 165 return TRUE; 166 } 167 168 public function visitSelectorSimplePseudoClass( 169 Ast\Selector\Simple\PseudoClass $class 170 ): void { 171 $this->_buffer .= ':'.$class->name; 172 if ($class->parameter) { 173 $this->_buffer .= '('; 174 $this->visit($class->parameter); 175 $this->_buffer .= ')'; 176 } 177 } 178 179 public function visitValueLiteral( 180 Ast\Value\Literal $literal 181 ): void { 182 $this->_buffer .= $this->quoteString($literal->value); 183 } 184 185 public function visitValueNumber( 186 Ast\Value\Number $literal 187 ): void { 188 $this->_buffer .= $literal->value; 189 } 190 191 public function visitEnterSelectorSimplePseudoClass( 192 Ast\Selector\Simple\PseudoClass $class 193 ): bool { 194 $this->_buffer .= ':'.$class->name.'('; 195 return TRUE; 196 } 197 198 public function visitLeaveSelectorSimplePseudoClass(): void { 199 $this->_buffer .= ')'; 200 } 201 202 public function visitValuePosition( 203 Ast\Value\Position $position 204 ): void { 205 $repeatsOddEven = $position->repeat === 2; 206 if ($repeatsOddEven && $position->add === 1) { 207 $this->_buffer .= 'odd'; 208 } elseif ($repeatsOddEven && $position->add === 0) { 209 $this->_buffer .= 'even'; 210 } elseif ($position->repeat === 0) { 211 $this->_buffer .= $position->add; 212 } elseif ($position->repeat === 1) { 213 $this->_buffer .= 'n'; 214 if ($position->add !== 0) { 215 $this->_buffer .= $position->add >= 0 216 ? '+'.$position->add : $position->add; 217 } 218 } else { 219 $this->_buffer .= $position->repeat.'n'; 220 if ($position->add !== 0) { 221 $this->_buffer .= $position->add >= 0 222 ? '+'.$position->add : $position->add; 223 } 224 } 225 } 226 227 public function visitValueLanguage( 228 Ast\Value\Language $language 229 ): void { 230 $this->_buffer .= ':lang('.$language->language.')'; 231 } 232 233 public function visitSelectorSimplePseudoElement( 234 Ast\Selector\Simple\PseudoElement $element 235 ): void { 236 $this->_buffer .= '::'.$element->name; 237 } 238 239 private function quoteString(string $string): string { 240 return '"'.str_replace(array('\\', '"'), array('\\\\', '\\"'), $string).'"'; 241 } 242 } 243} 244