: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// a space separated list of selectors
protected function tag(&$tag, $simple = false) {
if ($simple) $chars = '^@,:;{}\][>\(\) "\''; else
if (!$simple && $this->tagExpression($tag)) {
while ($this->tagBracket($first)) $parts[] = $first;
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
if ($this->match('([' . $chars . '0-9][' . $chars . ']*)', $m)) {
while ($this->tagBracket($brack)) {
if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") {
if ($this->interpolation($interp)) {
$interp[2] = true; // don't unescape
if ($this->literal("@")) {
if ($this->unit($unit)) { // for keyframes
$this->eatWhiteDefault = $oldWhite;
$tag = trim(implode($parts));
protected function func(&$func) {
if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) {
$sPreArgs = $this->seek();
// this ugly nonsense is for ie filter properties
if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) {
if ($this->expressionList($value)) {
if (!$this->literal(',')) break;
if ($this->literal(')')) {
} elseif ($fname == 'url') {
// couldn't parse and in url? treat as string
if ($this->openString(")", $string) && $this->literal(")")) {
// consume a less variable
protected function variable(&$name) {
if ($this->literal($this->lessc->vPrefix, false) && ($this->variable($sub) || $this->keyword($name))) {
$name = $this->lessc->vPrefix . $name;
* Consume an assignment operator
* Can optionally take a name that will be set to the current property name
protected function assign($name = null) {
if ($name) $this->currentProperty = $name;
return $this->literal(':') || $this->literal('=');
protected function keyword(&$word) {
if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) {
// consume an end of statement delimiter
protected function end() {
if ($this->literal(';')) {
} elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') {
// if there is end of file or a closing block next then we don't need a ;
protected function guards(&$guards) {
if (!$this->literal("when")) {
while ($this->guardGroup($g)) {
if (!$this->literal(",")) break;
if (count($guards) == 0) {
// a bunch of guards that are and'd together
// TODO rename to guardGroup
protected function guardGroup(&$guardGroup) {
while ($this->guard($guard)) {
if (!$this->literal("and")) break;
if (count($guardGroup) == 0) {
protected function guard(&$guard) {
$negate = $this->literal("not");
if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) {
if ($negate) $guard = array(
/* raw parsing functions */
protected function literal($what, $eatWhitespace = null) {
if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
// shortcut on single letter
if (!isset($what[1]) && isset($this->buffer[$this->count])) {
if ($this->buffer[$this->count] == $what) {
if (!isset(self::$literalCache[$what])) {
self::$literalCache[$what] = LessCompiler::preg_quote($what);
return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
protected function genericList(&$out, $parseItem, $delim = "", $flatten = true) {
while ($this->$parseItem($value)) {
if (!$this->literal($delim)) break;
if (count($items) == 0) {
if ($flatten && count($items) == 1) {
// advance counter to next occurrence of $what
// $until - don't include $what in advance
// $allowNewline, if string, will be used as valid char set
protected function to($what, &$out, $until = false, $allowNewline = false) {
if (is_string($allowNewline)) {
$validChars = $allowNewline;
$validChars = $allowNewline ? "." : "[^\n]";
if (!$this->match('(' . $validChars . '*?)' . LessCompiler::preg_quote($what), $m, !$until)) return false;
if ($until) $this->count -= strlen($what); // give back $what
// try to match something on head of buffer
protected function match($regex, &$out, $eatWhitespace = null) {
if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault;
$r = '/' . $regex . ($eatWhitespace && !$this->writeComments ? '\s*' : '') . '/Ais';
if (preg_match($r, $this->buffer, $out, 0, $this->count)) {
$this->count += strlen($out[0]);
if ($eatWhitespace && $this->writeComments) $this->whitespace();
protected function whitespace() {
if ($this->writeComments) {
while (preg_match(self::$whitePattern, $this->buffer, $m, 0, $this->count)) {
if (isset($m[1]) && empty($this->commentsSeen[$this->count])) {
$this->commentsSeen[$this->count] = true;
$this->count += strlen($m[0]);
return strlen($m[0]) > 0;
// match something without consuming it
protected function peek($regex, &$out = null, $from = null) {
if (is_null($from)) $from = $this->count;
$r = '/' . $regex . '/Ais';
$result = preg_match($r, $this->buffer, $out, 0, $from);
// seek to a spot in the buffer or return where we are on no argument
protected function seek($where = null) {
if ($where === null) return $this->count; else $this->count = $where;
public function throwError($msg = "parse error", $count = null) {
$count = is_null($count) ? $this->count : $count;
$line = $this->line + substr_count(substr($this->buffer, 0, $count), "\n");
if (!empty($this->sourceName)) {
$loc = "$this->sourceName on line $line";
// TODO this depends on $this->count
if ($this->peek("(.*?)(\n|$)", $m, $count)) {
throw new Exception("$msg: failed at `$m[1]` $loc<br>FILE: <strong>{$this->lessc->sourceParser->sourceName}</strong>");
throw new Exception("$msg: $loc<br>FILE: <strong>{$this->lessc->sourceParser->sourceName}</strong>");
protected function pushBlock($selectors = null, $type = null) {
$b->id = self::$nextBlockId++;
$b->isVararg = false; // TODO: kill me from here
// push a block that doesn't multiply tags
protected function pushSpecialBlock($type) {
return $this->pushBlock(null, $type);
// append a property to the current block
protected function append($prop, $pos = null) {
if ($pos !== null) $prop[-1] = $pos;
$this->env->props[] = $prop;
// pop something off the stack
protected function pop() {
$this->env = $this->env->parent;
// remove comments from $text
// todo: make it work for all functions, not just url
protected function removeComments($text) {
foreach ($look as $token) {
$pos = strpos($text, $token);
if (!isset($min) || $pos < $min[1]) $min = array(
if (is_null($min)) break;
if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) $count += strlen($m[0]) - strlen($min[0]);
if (preg_match('/' . $min[0] . '.*?' . $min[0] . '/', $text, $m, 0, $count)) $count += strlen($m[0]) - 1;
$skip = strpos($text, "\n", $count);