Call.php 4.9 KB

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