Call.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. }