/** * Compiles given AngularJS expression with given Scope data. * * @param string $expression Expression string to compile. * @param Scope $scope Scope object containing data for expression. * @return string Compiled expression. * @throws InvalidExpressionException Exception is thrown if expression contains brackets "()" * to prevent eval from calling functions for security reasons. */ public function compile($expression, Scope $scope) { $expression = trim($expression, '{} '); /** * We forbid any function calls inside expressions. */ if (preg_match('/[\\(\\)]/', $expression) === 1) { throw new InvalidExpressionException('Expression contains one or both of forbidden characters: "()"!'); } $expressionOperators = '/[^' . preg_quote('+-/*!=&|?:<>%,\'"', '/') . ']+/'; preg_match_all($expressionOperators, $expression, $expressionMatches); /** * I no special operators found just replace access string with value. */ if (isset($expressionMatches[0][0]) && $expressionMatches[0][0] === $expression) { $renderedExpression = $scope->getData($expression); } else { /** * Check each expression we can replace. */ foreach ($expressionMatches[0] as $expressionMatch) { $expressionMatch = trim($expressionMatch); /** * If we can replace it with data - do this and format variable if required for later eval. */ if (is_numeric($expressionMatch) === false) { $expression = str_replace($expressionMatch, var_export($scope->getData($expressionMatch), true), $expression); } } $renderedExpression = $this->evalaluate($expression); } return $renderedExpression; }
/** * Compiles AngularJS ng-class-odd attributes by evaluating expression inside it * and setting element's class attribute if element has odd index. * * @param \DOMElement $element DOM element to compile. * @param Scope $scope Scope object containing data for expression. * @return \DOMElement Compiled DOM element. */ public function compile(\DOMElement $element, Scope $scope) { if ($scope->getData('$odd') == true) { $ngClass = new NgClass($this->phCompile); $ngClass->compile($element, $scope); } }
/** * @covers PhCompile\Template\Directive\NgClass::compile * @dataProvider compileExceptionProvider * @expectedException PhCompile\Template\Expression\InvalidExpressionException */ public function testCompileException($scopeData, $classString) { $this->scope->setData($scopeData); $document = Utils::loadHTML('<span ng-class="' . $classString . '"></span>'); $element = $document->getElementsByTagName('span')->item(0); $this->ngClass->compile($element, $this->scope); }
/** * @covers PhCompile\Template\Directive\NgValue::compile * @dataProvider compileProvider */ public function testCompile($scopeData, $bindString, $expected) { $this->scope->setData($scopeData); $document = Utils::loadHTML('<span ng-value="' . $bindString . '"></span>'); $element = $document->getElementsByTagName('span')->item(0); $compiledHtml = Utils::saveHTML($this->ngValue->compile($element, $this->scope)->ownerDocument); $expectedHtml = '<span ng-value="' . $bindString . '" value="' . $expected . '"></span>'; $this->assertSame($expectedHtml, $compiledHtml); }
/** * @covers PhCompile\Template\Directive\NgHide::compile * @dataProvider compileProvider */ public function testCompile($scopeData, $expression, $expectedClass) { $this->scope->setData($scopeData); $document = Utils::loadHTML('<span ng-hide="' . $expression . '" class=""></span>'); $element = $document->getElementsByTagName('span')->item(0); $this->ngHide->compile($element, $this->scope); $renderedHtml = Utils::saveHTML($element->ownerDocument); $expectedHtml = '<span ng-hide="' . $expression . '" class="' . $expectedClass . '"></span>'; $this->assertSame($expectedHtml, $renderedHtml); }
/** * @covers PhCompile\Template\Directive\NgRepeat::compile * @dataProvider repeatSpectialPropertiesProvider */ public function testRepeatSpecialProperties($expression, $expectedArray) { $this->scope->setData(array('bar' => array(1, 2, 3, 4, 5, 6))); $document = Utils::loadHTML('<span ng-repeat="n in bar">{{' . $expression . '}}</span>'); $element = $document->getElementsByTagName('span')->item(0); $renderClass = $this->phCompile->getConfig('compile.class'); $renderAttr = $this->phCompile->getConfig('compile.attr'); $this->ngRepeat->compile($element, $this->scope); $compiledHtml = Utils::saveHTML($document); $expectedHtml = '<span ng-repeat="n in bar" class="ng-hide">{{' . $expression . '}}</span>'; for ($i = 0; $i < 6; $i++) { $expectedHtml .= '<span class="' . $renderClass . '"><span ' . $renderAttr . '="' . $expression . '">' . $expectedArray[$i] . '</span></span>'; } $this->assertSame($expectedHtml, $compiledHtml); }
/** * Returns value corresponding to given access string. * * @param string|null $accessString Access string to wanted config value, * null if you want entire config array. * @return mixed Value corresponding to given access string or null if value * does not exist. */ public function getConfig($accessString = null) { return $this->config->getData($accessString); }
/** * Sets special properties for given scope based on current ng-repeat cycle. * * @param Scope $scope Scope to set data to. * @param int $repeatCount Size of the array we iterate over. * @param int $repeatIndex Current index of the array we iterate over. * @return Scope Scope object with set values. */ protected function setScopeSpecial(Scope $scope, $repeatCount, $repeatIndex) { $scopeData['$index'] = $repeatIndex; $scopeData['$first'] = $repeatIndex === 0; $scopeData['$last'] = $repeatIndex === $repeatCount - 1; $scopeData['$middle'] = !($scopeData['$first'] || $scopeData['$last']); $scopeData['$even'] = $repeatIndex % 2 === 0; $scopeData['$odd'] = !$scopeData['$even']; $scope->setData($scopeData); return $scope; }
/** * @covers PhCompile\Scope::hasData * @dataProvider hasDataProvider * @depends testGetData */ public function testHasData($data, $accessString, $expected) { $this->scope->setData($data); $this->assertSame($expected, $this->scope->hasData($accessString)); }