Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

Manipulator.php

Namespace

Behat\Mink\Selector\Xpath

File

vendor/behat/mink/src/Selector/Xpath/Manipulator.php

View source
<?php


/*
 * This file is part of the Mink package.
 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Behat\Mink\Selector\Xpath;


/**
 * XPath manipulation utility.
 *
 * @author Graham Bates
 * @author Christophe Coevoet <stof@notk.org>
 */
class Manipulator {
    
    /**
     * Regex to find union operators not inside brackets.
     */
    const UNION_PATTERN = '/\\|(?![^\\[]*\\])/';
    
    /**
     * Prepends the XPath prefix to the given XPath.
     *
     * The returned XPath will match elements matching the XPath inside an element
     * matching the prefix.
     *
     * @param string $xpath
     * @param string $prefix
     *
     * @return string
     */
    public function prepend(string $xpath, string $prefix) {
        $expressions = array();
        // If the xpath prefix contains a union we need to wrap it in parentheses.
        if (preg_match(self::UNION_PATTERN, $prefix)) {
            $prefix = '(' . $prefix . ')';
        }
        // Split any unions into individual expressions.
        foreach ($this->splitUnionParts($xpath) as $expression) {
            $expression = trim($expression);
            $parenthesis = '';
            // If the union is inside some braces, we need to preserve the opening braces and apply
            // the prefix only inside it.
            if (preg_match('/^[\\(\\s*]+/', $expression, $matches)) {
                $parenthesis = $matches[0];
                $expression = substr($expression, strlen($parenthesis));
            }
            // add prefix before element selector
            if (0 === strpos($expression, '/')) {
                $expression = $prefix . $expression;
            }
            else {
                $expression = $prefix . '/' . $expression;
            }
            $expressions[] = $parenthesis . $expression;
        }
        return implode(' | ', $expressions);
    }
    
    /**
     * Splits the XPath into parts that are separated by the union operator.
     *
     * @param string $xpath
     *
     * @return string[]
     */
    private function splitUnionParts(string $xpath) : array {
        if (false === strpos($xpath, '|')) {
            return array(
                $xpath,
            );
            // If there is no pipe in the string, we know for sure that there is no union
        }
        $xpathLen = strlen($xpath);
        $openedBrackets = 0;
        // Consume whitespaces chars at the beginning of the string (this is the list of chars removed by trim() by default)
        $startPosition = strspn($xpath, " \t\n\r\x00\v");
        $unionParts = array();
        for ($i = $startPosition; $i <= $xpathLen; ++$i) {
            // Consume all chars until we reach a quote, a bracket or a pipe
            $i += strcspn($xpath, '"\'[]|', $i);
            if ($i < $xpathLen) {
                switch ($xpath[$i]) {
                    case '"':
                    case "'":
                        // Move to the end of the string literal
                        if (false === ($i = strpos($xpath, $xpath[$i], $i + 1))) {
                            return array(
                                $xpath,
                            );
                            // The XPath expression is invalid, don't split it
                        }
                        continue 2;
                    case '[':
                        ++$openedBrackets;
                        continue 2;
                    case ']':
                        --$openedBrackets;
                        continue 2;
                }
            }
            if ($openedBrackets) {
                continue;
            }
            $unionParts[] = substr($xpath, $startPosition, $i - $startPosition);
            if ($i === $xpathLen) {
                return $unionParts;
            }
            // Consume any whitespace chars after the pipe
            $i += strspn($xpath, " \t\n\r\x00\v", $i + 1);
            $startPosition = $i + 1;
        }
        return array(
            $xpath,
        );
        // The XPath expression is invalid
    }

}

Classes

Title Deprecated Summary
Manipulator XPath manipulation utility.

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal