PHP функция parse_str (), позволяющая передавать сокращенный массив - PullRequest
0 голосов
/ 05 августа 2020

Мы принимаем строки из шаблонов в механизм разметки, который позволяет передавать конфигурацию в «простой» форме.

Механизм анализирует строки через PHP, используя адаптированную версию Функция parse_str () - чтобы мы могли проанализировать любую комбинацию строк ниже:

config=posts_per_page:"5",default:"No questions yet -- once created they will appear here."&markup->template="{{ questions }}"

дает:

Array(
[config] => Array
    (
        [posts_per_page] => 5
        [default] => No questions yet -- once created they will appear here.
    )

[markup] => Array
    (
        [template] => {{ questions }}
    )
)

OR:

config->default=all:"<p class='ml-3'>No members here yet...</p>"

Чтобы получить:

Array 
[config] => Array
    (
        [default] => Array
            (
                [all] => <p class='ml-3'>No members here yet...</p>
            )

    )
)

Другой:

config=>handle:"medium"

Возвращает:

Array (
[config] => Array
    (
        [>handle] => medium
    )
)

Строки могут передаваться с пробелами (и многострочными пробелами), и должны передаваться строковые параметры между "двойными кавычками", чтобы сохранить естественный интервал - мы запускаем следующую preg_replace в строке, прежде чем она будет передана методу parse_str:

// strip white spaces from data that is not passed inside double quotes ( "data" ) ##
$string = preg_replace( '~"[^"]*"(*SKIP)(*F)|\s+~', "", $string );

Пока все хорошо - пока мы не попытаемся передать "разделитель" "внутри строкового значения, то оно обрабатывается буквально - например, следующая строка возвращает поврежденный массив:

config=posts_per_page:"5",default:"No questions yet -- once created, they will appear here."&markup->template="{{ questions }}"

Возвращает следующий массив:

Array (
[config] => Array
    (
        [posts_per_page] => 5
        [default] => No questions yet -- once created
        [ they will appear here."] => 
    )

[markup] => Array
    (
        [template] => {{ questions }}
    )
)

Знак«, »был обрабатывались буквально, и строка была сломана int o дополнительная часть массива.

Одним из простых решений является создание разделителей и операторов с меньшей вероятностью конфликта со строковыми значениями - например, изменение "," на "@@@" - но одной важной частью используемая разметка заключается в том, что ее легко писать и читать - это предполагаемый вариант использования для интерфейсных разработчиков, чтобы передать простые аргументы синтаксическому анализатору шаблона - это одна из причин, по которой мы пытались избежать JSON - что, конечно, является хорошо подходит с точки зрения передачи данных, но его трудно читать и писать - конечно, это утверждение субъективно и открыто для мнений :)

Вот метод parse_str:

public static function parse_str( $string = null ) {

    // h::log($string);

    // delimiters ##
    $operator_assign = '=';
    $operator_array = '->';
    $delimiter_key = ':';
    $delimiter_and_property = ',';
    $delimiter_and_key = '&';

    // check for "=" delimiter ##
    if( false === strpos( $string, $operator_assign ) ){

        h::log( 'e:>Passed string format does not include asssignment operator "'.$operator_assign.'" -- '.$string );

        return false;

    }

    # result array
    $array = [];
  
    # split on outer delimiter
    $pairs = explode( $delimiter_and_key, $string );
  
    # loop through each pair
    foreach ( $pairs as $i ) {

        # split into name and value
        list( $key, $value ) = explode( $operator_assign, $i, 2 );

        // what about array values ##
        // example -- sm:medium, lg:large
        if( false !== strpos( $value, $delimiter_key ) ){

            // temp array ##
            $value_array = [];  

            // split value into an array at "," ##
            $value_pairs = explode( $delimiter_and_property, $value );

            // h::log( $value_pairs );

            # loop through each pair
            foreach ( $value_pairs as $v_pair ) {

                // h::log( $v_pair ); // 'sm:medium'

                # split into name and value
                list( $value_key, $value_value ) = explode( $delimiter_key, $v_pair, 2 );

                $value_array[ $value_key ] = $value_value;

            }

            // check if we have an array ##
            if ( is_array( $value_array ) ){

                $value = $value_array;

            }

        }
     
        // $key might be in part__part format, so check ##
        if( false !== strpos( $key, $operator_array ) ){

            // explode, max 2 parts ##
            $md_key = explode( $operator_array, $key, 2 );

            # if name already exists
            if( isset( $array[ $md_key[0] ][ $md_key[1] ] ) ) {

                # stick multiple values into an array
                if( is_array( $array[ $md_key[0] ][ $md_key[1] ] ) ) {
                
                    $array[ $md_key[0] ][ $md_key[1] ][] = $value;
                
                } else {
                
                    $array[ $md_key[0] ][ $md_key[1] ] = array( $array[ $md_key[0] ][ $md_key[1] ], $value );
                
                }

            # otherwise, simply stick it in a scalar
            } else {

                $array[ $md_key[0] ][ $md_key[1] ] = $value;

            }

        } else {

            # if name already exists
            if( isset($array[$key]) ) {

                # stick multiple values into an array
                if( is_array($array[$key]) ) {
                
                    $array[$key][] = $value;
                
                } else {
                
                    $array[$key] = array($array[$key], $value);
                
                }

            # otherwise, simply stick it in a scalar
            } else {

                $array[$key] = $value;

            }
          
        }
    }

    // h::log( $array );
  
    # return result array
    return $array;

  }

Я постараюсь пропустить разделение строки между «двойными кавычками» - возможно, с помощью другого регулярного выражения, но, возможно, есть другие потенциальные подводные камни, которые могут не сделать этот подход жизнеспособным в долгосрочной перспективе - любая помощь с радостью принимается!

1 Ответ

0 голосов
/ 05 августа 2020

Одно из решений - изменить следующее:

с:

$value_pairs = explode( $delimiter_and_property, $value );

на:

$value_pairs = self::quoted_explode( $value, $delimiter_and_property, '"' );

, который вызывает новый метод, найденный в другом ответе SO (ссылка в блоке комментариев):

...