1<?php 2 3class Less_Tree_Mixin_Call extends Less_Tree { 4 5 public $selector; 6 public $arguments; 7 public $index; 8 public $currentFileInfo; 9 10 public $important; 11 public $type = 'MixinCall'; 12 13 /** 14 * less.js: tree.mixin.Call 15 * 16 */ 17 public function __construct( $elements, $args, $index, $currentFileInfo, $important = false ) { 18 $this->selector = new Less_Tree_Selector( $elements ); 19 $this->arguments = $args; 20 $this->index = $index; 21 $this->currentFileInfo = $currentFileInfo; 22 $this->important = $important; 23 } 24 25 // function accept($visitor){ 26 // $this->selector = $visitor->visit($this->selector); 27 // $this->arguments = $visitor->visit($this->arguments); 28 //} 29 30 public function compile( $env ) { 31 $rules = array(); 32 $match = false; 33 $isOneFound = false; 34 $candidates = array(); 35 $defaultUsed = false; 36 $conditionResult = array(); 37 38 $args = array(); 39 foreach ( $this->arguments as $a ) { 40 $args[] = array( 'name' => $a['name'], 'value' => $a['value']->compile( $env ) ); 41 } 42 43 foreach ( $env->frames as $frame ) { 44 45 $mixins = $frame->find( $this->selector ); 46 47 if ( !$mixins ) { 48 continue; 49 } 50 51 $isOneFound = true; 52 $defNone = 0; 53 $defTrue = 1; 54 $defFalse = 2; 55 56 // To make `default()` function independent of definition order we have two "subpasses" here. 57 // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`), 58 // and build candidate list with corresponding flags. Then, when we know all possible matches, 59 // we make a final decision. 60 61 $mixins_len = count( $mixins ); 62 for ( $m = 0; $m < $mixins_len; $m++ ) { 63 $mixin = $mixins[$m]; 64 65 if ( $this->IsRecursive( $env, $mixin ) ) { 66 continue; 67 } 68 69 if ( $mixin->matchArgs( $args, $env ) ) { 70 71 $candidate = array( 'mixin' => $mixin, 'group' => $defNone ); 72 73 if ( $mixin instanceof Less_Tree_Ruleset ) { 74 75 for ( $f = 0; $f < 2; $f++ ) { 76 Less_Tree_DefaultFunc::value( $f ); 77 $conditionResult[$f] = $mixin->matchCondition( $args, $env ); 78 } 79 if ( $conditionResult[0] || $conditionResult[1] ) { 80 if ( $conditionResult[0] != $conditionResult[1] ) { 81 $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse; 82 } 83 84 $candidates[] = $candidate; 85 } 86 } else { 87 $candidates[] = $candidate; 88 } 89 90 $match = true; 91 } 92 } 93 94 Less_Tree_DefaultFunc::reset(); 95 96 $count = array( 0, 0, 0 ); 97 for ( $m = 0; $m < count( $candidates ); $m++ ) { 98 $count[ $candidates[$m]['group'] ]++; 99 } 100 101 if ( $count[$defNone] > 0 ) { 102 $defaultResult = $defFalse; 103 } else { 104 $defaultResult = $defTrue; 105 if ( ( $count[$defTrue] + $count[$defFalse] ) > 1 ) { 106 throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format( $args ) . '`' ); 107 } 108 } 109 110 $candidates_length = count( $candidates ); 111 $length_1 = ( $candidates_length == 1 ); 112 113 for ( $m = 0; $m < $candidates_length; $m++ ) { 114 $candidate = $candidates[$m]['group']; 115 if ( ( $candidate === $defNone ) || ( $candidate === $defaultResult ) ) { 116 try{ 117 $mixin = $candidates[$m]['mixin']; 118 if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) { 119 $mixin = new Less_Tree_Mixin_Definition( '', array(), $mixin->rules, null, false ); 120 $mixin->originalRuleset = $mixins[$m]->originalRuleset; 121 } 122 $rules = array_merge( $rules, $mixin->evalCall( $env, $args, $this->important )->rules ); 123 } catch ( Exception $e ) { 124 // throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']); 125 throw new Less_Exception_Compiler( $e->getMessage(), null, null, $this->currentFileInfo ); 126 } 127 } 128 } 129 130 if ( $match ) { 131 if ( !$this->currentFileInfo || !isset( $this->currentFileInfo['reference'] ) || !$this->currentFileInfo['reference'] ) { 132 Less_Tree::ReferencedArray( $rules ); 133 } 134 135 return $rules; 136 } 137 } 138 139 if ( $isOneFound ) { 140 throw new Less_Exception_Compiler( 'No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo ); 141 142 } else { 143 throw new Less_Exception_Compiler( trim( $this->selector->toCSS() ) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index ); 144 } 145 146 } 147 148 /** 149 * Format the args for use in exception messages 150 * 151 */ 152 private function Format( $args ) { 153 $message = array(); 154 if ( $args ) { 155 foreach ( $args as $a ) { 156 $argValue = ''; 157 if ( $a['name'] ) { 158 $argValue .= $a['name'] . ':'; 159 } 160 if ( is_object( $a['value'] ) ) { 161 $argValue .= $a['value']->toCSS(); 162 } else { 163 $argValue .= '???'; 164 } 165 $message[] = $argValue; 166 } 167 } 168 return implode( ', ', $message ); 169 } 170 171 /** 172 * Are we in a recursive mixin call? 173 * 174 * @return bool 175 */ 176 private function IsRecursive( $env, $mixin ) { 177 foreach ( $env->frames as $recur_frame ) { 178 if ( !( $mixin instanceof Less_Tree_Mixin_Definition ) ) { 179 180 if ( $mixin === $recur_frame ) { 181 return true; 182 } 183 184 if ( isset( $recur_frame->originalRuleset ) && $mixin->ruleset_id === $recur_frame->originalRuleset ) { 185 return true; 186 } 187 } 188 } 189 190 return false; 191 } 192 193} 194