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