Verification: a143cc29221c9be0

Php code for html link

Php code for html link

Введение

Если вы не знакомы с устройством html парсеров и не прочитали предыдущею главу, рекомендую почитать. Я обновил в ней картинки, так что теперь воспринимать информацию там приятнее.

В этой статье будет введен новый ряд терминов:

  • Цепочка — последовательность любых символов.
  • Состояние — условие, от которого будет зависеть дальнейшая обработка цепочки парсером.
  • Токен — массив, содержащий информацию о теге, комментарии или тексте. Также токен часто называют “Словом”.
  • Стек — массив, содержащий токены.
  • Разграничительный символ — символ, после которого следующая цепочка обрабатывается как новая цепочка.
  • Алфавит — список символов, которые имеют первостепенную важность для парсера.

Если термины из списка вам не понятны, не волнуйтесь: в контексте все станет ясно.

Исходный код доступен на github.

Главные переменные парсера

Перед тем как писать парсер, следует определится с главными переменными, чтобы потом отталкиваться от них при дальнейшем написании.

А их будет четыре:

  1. $__SOURCE_TEXT — Содержит в себе текст исходного документа.
  2. $__DOM — Содержит в себе полученный в результате парсинга массив с dom деревом.
  3. $__ENABLE_COMMENTS — Означает, включена ли функция отображения комментариев или нет.
  4. $__ESCAPE_SYMBOLS — Массив со специальными символами и пробелом.
  5. $__MANDATORY_OPEN_ELEMENTS — Массив из четырех ячеек, обозначающие наличие обязательных открывающих тегов в документе.
  6. $__MANDATORY_CLOSE_ELEMENTS — Массив из трёх ячеек, обозначающие наличие обязательных закрывающих тегов в документе.

Класс парсера и его конструктор

Для того, чтобы потом было легче добавлять функционал, напишем класс парсера. В начале там будет функция отправки запросов, но постепенно туда будет записываться новый функционал.


Давайте кратко вспомним, как работает анализ

Как вы помните, в первой части статьи, работа парсера была разделена на два этапа. Так вот, в начале я буду говорить о первом этапе, об отделении текста от тегов.

Чтобы дальше было проще, первый этап я буду называть «Анализом».

Итак, как работает анализ в парсере. Сначала, он берет символ. Если этот символ равен "

После работы функция выдает токен, который отправляется в стек. Токен содержит в себе информацию о теге: его название, классы, идентификаторы и другие атрибуты.

Обобщим: анализатор видит "токен, и дальше отдает токен стеку.

Работа анализа «Изнутри»

Чтобы написать функцию анализа, стоит обратиться к теории конечных автоматов. Если говорить просто, то во время работы нашего алгоритма у него будет состояние. В зависимости от состояния, после того как алгоритм дошел до разграничительного символа, будет зависеть то, что он будет делать с полученной информацией. Например, если до пробела была цепочка, где в начале был символ "

Работа с кавычками в значении атрибута

Стоит об этом поговорить. Скорее всего вы знаете, что в html можно написать, например, такое:

И нельзя написать такое:

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

Счетчик кавычек, как понятно из названия, будет их считать. Пока количество кавычек не будет четным, парсер будет считать все символы значением атрибута. Если все правильно, значит значение атрибута будет записано в токен.

Тип открывающей кавычки будет означать, какая изначально кавычка была использована при написании. Значение атрибута не будет записано, пока не будет найдена закрывающая кавычка, тип которой совпадает с изначальной.

Обязательные и необязательные теги в документе

Скорее всего вы знаете, что на сайте, написанном на html всегда должны быть определенные теги. Если быть точнее, то всего три: ,

и . Если таковых нет в документе, либо присутствуют только закрывающие или открывающие обязательные теги, они ставятся в определенных местах. Давайте посмотрим, в каких:
  1. — Этот тег ставиться в конце и начале документа(после ), если после него не идет комментарий. В ином случаи поставиться ровно после комментария.
  2. — Ставится в месте, где есть теги , ,
  3. — ставится после head и до закрывающего .

Также стоит упомянуть, что помимо обязательных тегов в документе еще должна быть декларация типа документа — . В принципе, в нашем скрипте она практически никакой роли играть не будет, но в любом случаи стоит предусмотреть её вставку при необходимости.

Помимо обязательных открывающий тегов в html присутствуют еще и необязательные открывающие теги. Их несколько:

  1. — Ставится после закрывающего , либо если такового тега нет перед контентом таблицы.
  2. — ставиться перед .
  3. Теперь давайте посмотрим на реализацию. Для начала, нужно как-то узнать, есть ли нужные теги в документе, чтобы потом не добавлять лишние теги.

    Эта часть кода взята из функции анализа, дальше в статье вы увидите, где она будет находиться.

    Вы могли заметить, что в проверке не участвуют теги

    и . Они не являются обязательными, более того, их вставка может понадобится несколько раз в документе, поэтому их наличие и вставка будут проверяться динамически, во время выполнения функции вставки тегов.

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

    и . Все будет работать очень просто: так как эти теги встречаются в документе только в теге , то сначала функция будет проверять, есть ли такой тег в документе или нет. Если да, то она начинает цикл, в котором будут происходить следующие действия: сначала, функция должна будет проверить наличие тега в таблице. Если он есть, то добавлять еще один такой же тег бессмысленно. Если тег закрыт, то алгоритм начинает заново проверять наличие этого тега где-либо еще в таблице. Параллельно с этим функция также будет проверять наличие контента в таблице. Если контент в таблице присутствует и находится в тегах или , то вставлять смысла нет. Если же нет, то алгоритм вставит этот тег в нужное место в документе. Вот и все, давайте приступать к коду.

    Необязательные закрывающие теги

    В спецификации html говорится, что некоторым тегам при определенных условиях закрывающие теги не нужны. Давайте реализуем данный механизм и в нашем парсере.

    Но сначала давайте определим, при каких условиях можно не писать закрывающий тег. Эти условия в основном одни и те же для большинства тегов, так что будет руководствоваться исходя из них. Есть еще пара тегов с иными условиями, о них поговорим отдельно.

    Давайте взглянем на эти условия:

    Закрывающий тег elem может быть опущен, если следующий тег за ним elem или если в родительском элементе нет больше элементов.

    Elem в данном контексте обозначает тег, закрывающей тег которого необязателен. Он может быть один, а может быть и целая группа тегов(

    ,
    и т.п).

    Также стоит помнить, что у некоторый тегов есть, как я его назвал, подуровень. Например, тег

  4. может находиться в тегах
      или
        , и это нужно иметь в виду. Чтобы все стало ясно, давайте посмотрим на пример:

      1. Теперь давайте посмотрим что будет, если считать что все теги li находятся на “одном” уровне:

        • Это, очевидно, неправильное поведение. А вот что будет если допустить существование «подуровней»:

        • Мне кажется, этот наглядный пример должен был донести смысл моих слов.

          Теперь давайте напишем простой скрипт для подобной обработки только для тега li и ul.

          Простая реализация механизма вставки закрывающих тегов

          Как вы видите, кода слишком много, и если мы задумаемся написать под каждый такой тег такие условия(а их больше десятка), то конечная функция по размерам будет как сам парсер. Так что с этим нужно разобраться. Вместо написания одних и тех же условий для каждого такого тега, лучше написать одну гибкую функцию сразу для всех. Она значительно облегчит навигацию по коду, но будет гораздо сложнее метода разработки условий под каждый тег отдельно. Перед тем как показать код, давайте спроектируем алгоритм ее работы. Для начала, нужно определится с тем, где именно функция будет выполняться. Будет лучше, если функция будет выполнятся перед началом работы рекурсии, чтобы потом не было проблем с зависимостями в массиве dom дерева. Поэтому функция будет выполняться сразу после работы функции построения стека. Так как тегов с необязательными закрывающими тегами много, стоит сделать массив со всеми такими тегами, а также с тегами «подуровня»(

            ,
            ), и с тегами, перед которыми нужно ставить закрывающий тег(
          1. ,
            ,
            и д.р).

            После того, как мы разобрались с основными моментами, стоит поговорит о самом алгоритме работы функции. Сначала функция будет брать токен. Далее она проверяет, была ли функция вызвана собой же, или она выполняется впервые. Если она вызвана собой же, значит токен в данный момент находится в подуровне и дальнейшие элементы стоит обрабатывать только в контексте подуровня. Если нет, значит это первый вызов функции, и токены нужно обрабатывать уже без контекста.

            Далее, давайте посмотрим, как происходит обработка токенов. После того, как функция поняла причину своего вызова, она берет токен и пытается понять, относится ли название тега в токене к названиям тегов в массиве функции. Если какое-либо название из массива подошло, начинается цикл, в котором будут искаться другие теги, которые могут быть связаны с найденным изначально. Они могут быть тегами подуровня, либо тегами, перед которыми нужно ставить закрывающий тег. Если найденный во время цикла тег является тегом подуровня, функция вызывает саму себя, задавая точку, где она остановилась а также говорит вызываемой функции, что та ее вызвала. Дальше вызванная функция обрабатывает теги в контексте подуровня, в результате выдавая точку завершения работы а также обработанный стек. После всего этого, функция дальше ищет подходящие теги на обработку. Если же был найден тег, перед которым нужно поставить закрывающий тег, функция сначала проверяет, не является ли этот тег закрывающим а также были ли такие же теги до этого. Благодаря таким проверкам можно избавиться от лишних закрывающих тегов.

            В принципе, это все. Остались только некоторые различия в обработке в контексте подуровня и в бесконтекстной обработке. Но они не слишком сильно отличаются, так что понять различия можно будет при просмотре кода.

            Caption и colgroup

            Теперь давайте поговорим про теги с иными условиями, благо их всего два —

        • . В спецификации сказано, что закрывающий тег несколько иначе. Спецификация говорит, что закрывающий тег может быть опущен если после него нету пробельного символа или комментария. Но тут ситуация неоднозначная, так как наш парсер пропускает все пробельные символы после тегов. Так что мы условимся, что в могут быть только теги и как только парсер найдет другой тег, он сразу же поставит перед ним закрывающий тег.

          Раз закончили, давайте смотреть на код.

          Функция вставки закрывающий тегов


          Дополнительные функции анализа


          Функция проверки тега на одиночность

          Эта функция проверяет, является ли тег одиночным. Если да, то выводит true.

          Функция для пропуска пробелов

          Эта функция необходима, чтобы в случаи если перед нужным символом стоит пробел, он пропускался. Понадобится в случаи, если между нужными цепочками будет много пробелов.

          Функция анализа

          Для начала создадим алфавит с символами, имеющими первостепенную важность для анализа. А этих символов будет семь:

          • " — Говорит о том, что дальше идет название тега.
          • "/" — Говорит о том, что тег является закрывающим.
          • ">" — Говорит о том, что дальше смысла в обработке нет, тег закрыт.
          • "=" — Говорит о том, что дальше будет идти значение атрибута.
          • "\'" — Говорит о том, что дальше идет значение атрибута.
          • "\"" — Имеет то же значение, что и прошлый вариант.
          • " " — Разграничительный символ.

          Стоит учитывать то, что любой из этих символов может встречаться в значении атрибута. Любой другой символ, не попадающий в этот список будет обрабатываться в зависимости от состояния.

          Теперь давайте определимся, сколько состояний будет у алгоритма анализа. А их будет всего четыре:

          • «attribute» — означает, что цепочка обрабатывается как атрибут.
          • «attribute_value» — означает, что цепочка обрабатывается как значение атрибута.
          • «tag» — означает, что цепочка обрабатывается как название тег.
          • «attribute_value_starting» — означает, что следующая цепочка будет обрабатываться как значение атрибута.

          Хорошо, со всем разобрались. Можно приступать к коду.

          Но что там по комментариям? Возможно вы спросите меня. Я не стал включать комментарии в эту функцию, банально потому, что это функция обработки тегов, когда у комментариев свой собственный синтаксис, и их нужно обрабатывать отдельной функцией. Об этом пойдет речь дальше.

          Так, а теперь построение стека

          Как строится стек я уже говорил ранее в первой главе. Так что нам осталось только реализовать этот алгоритм.

          Но давайте немного отвлечемся. Комментарии. Казалось бы, чего сложного? Но не все так просто.

          Посмотрите на комментарий ниже. Как, по вашему, должен такой комментарий обрабатываться?

          
          

          Если подумали, то посмотрите, как этот комментарий обрабатывает браузер и редактор.
          Вот как видит комментарий браузер:

          
          

          Как будто там и не было ничего. При этом, "-->" выносится как текст, а не комментарий.
          А вот как видит его редактор:

          
          

          Результат выглядит так же, как исходный пример. И назревает вопрос: как это обрабатывать? Я решил обрабатывать так же, как и редактор.

          А как будем обрабатывать теги

          Теперь стоит поговорить о дополнительных функциях к этому алгоритму. Их будет немного.

          Функция для обработки комментариев

          Нужна, как понятно из названия, для обработки комментариев.

          Функция для пропуска ненужных символов

          Пропускает все символы, указанные в параметрах функции в виде массива и выводит в результате строку без этих символов.

          С дополнительными функциями разобрались. Давайте писать код.

          Касательно «Костыля». Дело в том, что в алгоритме есть проверка, является ли текст пустым. Это сделано для того, чтобы после каждого токена не было пустых текстовых токенов с одними табами или другими специальными символами. Но '0' в строке php считает как пустую строку. Поэтому я добавил это дополнительно условие, чтобы если в тексте есть только нуль(не считая дополнительных символов вроде табов и пробелов), алгоритм его правильно обрабатывал.

          Построение dom дерева

          Половина парсера позади, что радует. Теперь давайте углубимся во вторую часть парсера. Для удобства, её я буду называть «рекурсией», так как, по сути, это одна большая рекурсия.

          Как говорилось в первой части, проблемы две. Первая с переизбытком тегов, вторая — их недостаток. Для решения этих проблем будет массив, в котором будут ячейки. В этих ячейках будет всего два параметра: Название тега и количество открытых тегов с таким именем. Также у нас будет переменная, которая будет хранить название родительского тега. Смотрите как это работает: если название закрывающего тега не совпадает с родительским тегом, значит где-то ошибка. Чтобы определить, какая именно ошибка, алгоритм будет смотреть массив со всеми открытыми тегами. Если один из тегов в массиве не был закрыт и он совпадает с текущим закрывающим тегом, значит нужно перед этим тегом поставить закрывающий тег родителя. В ином случаи значит что этот закрывающий тег лишний.

          Перед написанием кода следует поговорить о построении массива с открытыми тегами. Строится он очень просто: в массиве будет несколько ячеек, в каждой из которой будет по два параметра — название тега и количество открытых таких тегов. Для поиска определенного тега с его параметрами в массиве будет функция, которая будет возвращать количество открытых тегов с названием, указанным в аргументе функции. Если ячейки с таким тегом не будет, тогда парсер создаст новую ячейку с этим тегом.

          Функция поиска тега в массиве открытых тегов

          Что ж, раз со всем разобрались, давайте приступать к коду.

          Функция построения DOM дерева

          Итак, парсер готов. Как выглядит результат я думаю вы уже знаете, если читали прошлую главу. Остался только поиск. Давайте смотреть.

          Поиск элементов

          Как вы узнали из предыдущей главы, с поиском есть определенные сложности. Во-первых, поиск по классу и идентификатору отличаются от поиска по тегу. Во-вторых, нам нужно реализовать функцию поиска определенного элемента. Ну а третья причина состоит в том, что если мы захотим найти любой элемент в большом документе, то массив с результатами поиска будет огромен. Это происходит потому, что все дочерние токены находятся в подмассиве родительского токена, при этом во время поиска все неподходящие элементы будут пропускаться, но подходящие элементы из массива убираться не будут, сохраняя при этом вложенность. Таким образом, у нас может получиться такая картина, когда чтобы дойти до любого токена нам потребуется перелопатить кучу вложенных массивов.

          Поиск текста

          Давайте поговорим про поиск текста. 

          Поиск будет работать очень просто: если дать функции отдельный токен текста, то он его выведет. Если же функция получит dom дерево, то она пройдется по все дочерним токенам и, если найдет в дереве один либо больше токенов текста, выведет их в виде массива.

          Реализацию функции поиска текста вы сможете увидеть далее во время реализации классов поиска и дочернего элемента.

          Работа поиска «Изнутри»

          Как поверхностно работает поиск я объяснял в первой части цикла, так что давайте приступим непосредственно к реализации. Но перед этим стоит ввести некоторые переменные, с которыми будет работать алгоритм. А их будет пять:

          1. $__DOM — Содержит в себе массив с dom деревом, в котором впоследствии будет происходить поиск
          2. $__ELEMENT — Содержит в себе наименование элемента, который будет искать алгоритм(body,div и др).
          3. $__ELEMENT_TYPE — Означает тип элемента, который нужно искать(class, id, tag).
          4. $__ELEMENT_DOM — Содержит результаты поиска, чтобы в последующим, например, искать там дочерние элементы.
          5. $__ELEMENT_NUMBER — Означает номер элемента, который нужно найти.

          Для начала напишем класс, в котором в будущем реализуем алгоритм поиска. В нем будет конструктор.

          Отлично, класс написали. Теперь давайте поговорим про тонкости поиска.

          Сначала давайте поговорим про то, как найти определенный элемент по счету. Он может быть первый, второй, или пятый. Для этого добавим дополнительную переменную

          $point

          . Она отображает, на каком найденном элементе по счету находится поиск. Если мы указали в аргументах функции после элемента, который нам нужно найти число n, то поиск будет искать ровно до того момента, пока найденный элемент не будет по счету n-ым. При этом счетчик будет увеличиваться когда будет найден указанный в аргументах функции элемент. Ну и конечно, в результате поиска будет только этот элемент и его подмассив, если он присутствует.

          Раз со всем разобрались, давайте напишем поиск только по тегу, а потом уже по классу и идентификатору.

          Функция поиска только по тегу

          Окей, поиск по тегу написали. Теперь давайте посмотрим, как будет работать поиск по классу и идентификатору.

          Как вы видите, алгоритм поиска по классу слабо отличается от поиска по тегу. С поиском идентификатора все тоже самое — просто измените все слова «Класс» на «Тег».

          Теперь приступим к реализации. Эта функция будет вмещать в себе сразу поиск по трем параметрам: классу, тегу и идентификатору.

          Функция поиска по тегу, классу и идентификатору


          Поиск дочерних элементов

          Сразу стоит сказать, что все найденные элементы для парсера являются дочерний элементами. То есть, если вы хотите найти в определенном элементе определенный дочерний элемент, сначала вам нужно сказать парсеру, в каком элементе вы хотите начать поиск. Если вы не хотите этого делать, вам стоит воспользоваться аргументом функции поиска для нахождения определенного элемента, тогда вам не придется использовать дополнительную функцию поиска дочернего элемента для указания элемента, где именно нужно вести поиск.

          В целом, это все. Ничего сложного в написании класса нет.


          Тесты

          Вот мы и написали парсер! Теперь давайте проверим его в тестах. Тесты будут не только на производительность, но и на правильность выполнения. Давайте смотреть!

          Правильность выполнение

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

          Тест на недостающие теги

          Для теста возьмем вот такой html:

          Код

          
          
          	
          
          
          
          
          
          0
          Привет!

          Как видно в примере, одного тега не хватает. Давайте посмотрим, какой результат выведет наш парсер:

          Вывод

          Array
          (
          [0] => Array
          (
              [is_closing] => 
              [is_singleton] => 1
              [pointer] => 15
              [tag] => !DOCTYPE
              [html] => 1
          )
          
          [1] => Array
          (
              [is_closing] => 
              [is_singleton] => 
              [pointer] => 23
              [tag] => html
              [0] => Array
                  (
                      [0] => Array
                          (
                              [is_closing] => 
                              [is_singleton] => 
                              [pointer] => 32
                              [tag] => head
                              [0] => Array
                                  (
                                      [0] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 68
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 1
                                          )
          
                                      [1] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 104
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 2
                                          )
          
                                      [2] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 140
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 3
                                          )
          
                                      [3] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 167
                                              [tag] => link
                                              [rel] => stylesheet
                                          )
          
                                      [4] => Array
                                          (
                                              [is_closing] => 1
                                              [is_singleton] => 
                                              [pointer] => 177
                                              [tag] => head
                                          )
          
                                  )
          
                          )
          
                      [1] => Array
                          (
                              [is_closing] => 
                              [is_singleton] => 
                              [pointer] => 186
                              [tag] => body
                              [0] => Array
                                  (
                                      [0] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 
                                              [pointer] => 195
                                              [tag] => div
                                              [0] => Array
                                                  (
                                                      [0] => Array
                                                          (
                                                              [tag] => __TEXT
                                                              [0] => 0
          	
                                                          )
          
                                                      [1] => Array
                                                          (
                                                              [is_closing] => 
                                                              [is_singleton] => 
                                                              [pointer] => 227
                                                              [tag] => div
                                                              [id] => Array
                                                                  (
                                                                      [0] => somebody
                                                                  )
          
                                                              [0] => Array
                                                                  (
                                                                      [0] => Array
                                                                          (
                                                                              [tag] => __TEXT
                                                                              [0] => Привет!
          		
                                                                          )
          
                                                                      [1] => Array
                                                                          (
                                                                              [tag] => __COMMENT
                                                                              [0] => 
                                                                          )
          
                                                                      [2] => Array
                                                                          (
                                                                              [is_closing] => 1
                                                                              [is_singleton] => 
                                                                              [pointer] => 348
                                                                              [tag] => div
                                                                          )
          
                                                                  )
          
                                                          )
          
                                                      [2] => Array
                                                          (
                                                              [tag] => div //А вот и он
                                                              [is_closing] => 1
                                                          )
          
                                                  )
          
                                          )
          
                                      [1] => Array
                                          (
                                              [is_closing] => 1
                                              [is_singleton] => 
                                              [pointer] => 358
                                              [tag] => body
                                          )
          
                                  )
          
                          )
          
                      [2] => Array
                          (
                              [is_closing] => 1
                              [is_singleton] => 
                              [pointer] => 367
                              [tag] => html
                          )
          
                  )
          
          )
          
          )
          

          Отлично, парсер правильно обработал страницу.

          Теперь давайте посмотрим на работу парсера, когда тегов больше, чем нужно.
          Возьмем вот такой html:

          Код

          
          
          	
          
          
          
          
          
          Привет!

          В этом html, как видно, верстальщик немного переборщил с элементами. Впрочем, неважно. Давайте посмотрим, как парсер обработает это:

          Вывод

          Array
          (
          [0] => Array
          (
              [is_closing] => 
              [is_singleton] => 1
              [pointer] => 15
              [tag] => !DOCTYPE
              [html] => 1
          )
          
          [1] => Array
          (
              [is_closing] => 
              [is_singleton] => 
              [pointer] => 23
              [tag] => html
              [0] => Array
                  (
                      [0] => Array
                          (
                              [is_closing] => 
                              [is_singleton] => 
                              [pointer] => 32
                              [tag] => head
                              [0] => Array
                                  (
                                      [0] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 68
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 1
                                          )
          
                                      [1] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 104
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 2
                                          )
          
                                      [2] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 140
                                              [tag] => link
                                              [rel] => stylesheet
                                              [href] => 3
                                          )
          
                                      [3] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 1
                                              [pointer] => 167
                                              [tag] => link
                                              [rel] => stylesheet
                                          )
          
                                      [4] => Array
                                          (
                                              [is_closing] => 1
                                              [is_singleton] => 
                                              [pointer] => 177
                                              [tag] => head
                                          )
          
                                  )
          
                          )
          
                      [1] => Array
                          (
                              [is_closing] => 
                              [is_singleton] => 
                              [pointer] => 186
                              [tag] => body
                              [0] => Array
                                  (
                                      [0] => Array
                                          (
                                              [is_closing] => 
                                              [is_singleton] => 
                                              [pointer] => 195
                                              [tag] => div
                                              [0] => Array
                                                  (
                                                      [0] => Array
                                                          (
                                                              [is_closing] => 
                                                              [is_singleton] => 
                                                              [pointer] => 481
                                                              [tag] => div
                                                              [id] => Array
                                                                  (
                                                                      [0] => somebody
                                                                  )
          
                                                              [0] => Array
                                                                  (
                                                                      [0] => Array
                                                                          (
                                                                              [tag] => __TEXT
                                                                              [0] => Привет!
                                                                          )
          
                                                                      [1] => Array
                                                                          (
                                                                              [is_closing] => 1
                                                                              [is_singleton] => 
                                                                              [pointer] => 831
                                                                              [tag] => div
                                                                          )
          
                                                                  )
          
                                                          )
          
                                                      [1] => Array
                                                          (
                                                              [tag] => div
                                                              [is_closing] => 1
                                                          )
          
                                                  )
          
                                          )
          
                                      [1] => Array
                                          (
                                              [is_closing] => 1
                                              [is_singleton] => 
                                              [pointer] => 841
                                              [tag] => body
                                          )
          
                                  )
          
                          )
          
                      [2] => Array
                          (
                              [is_closing] => 1
                              [is_singleton] => 
                              [pointer] => 850
                              [tag] => html
                          )
          
                  )
          
          )
          
          )
          

          Чисто, аккуратно и по красоте. Что ж, раз так, давайте приступим к следующему тесту.

          Тест на хорошего верстальщика

          Наверное, самый интересный тест. Навряд ли конечно кто-нибудь будет так писать, но проверить нужно.
          Для этого теста возьмем вот такой html:

          Код

          
          
            
          
          
          
          
          

          Привет!

          А теперь давайте посмотрим на вывод парсера:

          Вывод

          Array
          (
          [0] => Array
          (
          [is_closing] => 
          [is_singleton] => 1
          [pointer] => 15
          [tag] => !DOCTYPE
          [html] => 1
          )
          
          [1] => Array
          (
          [is_closing] => 
          [is_singleton] => 
          [pointer] => 23
          [tag] => html
          [0] => Array
          (
          [0] => Array
          (
          [is_closing] => 
          [is_singleton] => 
          [pointer] => 33
          [tag] => head
          [0] => Array
              (
                  [0] => Array
                      (
                          [is_closing] => 
                          [is_singleton] => 1
                          [pointer] => 71
                          [tag] => link
                          [rel] => stylesheet
                          [href] => 1
                      )
          
                  [1] => Array
                      (
                          [is_closing] => 
                          [is_singleton] => 1
                          [pointer] => 109
                          [tag] => link
                          [rel] => stylesheet
                          [href] => 2
                      )
          
                  [2] => Array
                      (
                          [is_closing] => 
                          [is_singleton] => 1
                          [pointer] => 147
                          [tag] => link
                          [rel] => stylesheet
                          [href] => 3
                      )
          
                  [3] => Array
                      (
                          [is_closing] => 
                          [is_singleton] => 1
                          [pointer] => 176
                          [tag] => link
                          [rel] => stylesheet
                      )
          
                  [4] => Array
                      (
                          [is_closing] => 1
                          [is_singleton] => 
                          [pointer] => 187
                          [tag] => head
                      )
          
              )
          
          )
          
          [1] => Array
          (
          [is_closing] => 
          [is_singleton] => 
          [pointer] => 197
          [tag] => body
          [0] => Array
              (
                  [0] => Array
                      (
                          [is_closing] => 
                          [is_singleton] => 
                          [pointer] => 208
                          [tag] => div
                          [0] => Array
                              (
                                  [0] => Array
                                      (
                                          [is_closing] => 
                                          [is_singleton] => 
                                          [pointer] => 221
                                          [tag] => div
                                          [0] => Array
                                              (
                                                  [0] => Array
                                                      (
                                                          [is_closing] => 
                                                          [is_singleton] => 
                                                          [pointer] => 227
                                                          [tag] => span
                                                          [0] => Array
                                                              (
                                                                  [0] => Array
                                                                      (
                                                                          [tag] => span
                                                                          [is_closing] => 1
                                                                      )
          
                                                              )
          
                                                      )
          
                                                  [1] => Array
                                                      (
                                                          [is_closing] => 1
                                                          [is_singleton] => 
                                                          [pointer] => 233
                                                          [tag] => div
                                                      )
          
                                              )
          
                                      )
          
                                  [1] => Array
                                      (
                                          [is_closing] => 
                                          [is_singleton] => 
                                          [pointer] => 236
                                          [tag] => p
                                          [0] => Array
                                              (
                                                  [0] => Array
                                                      (
                                                          [is_closing] => 1
                                                          [is_singleton] => 
                                                          [pointer] => 247
                                                          [tag] => p
                                                      )
          
                                              )
          
                                      )
          
                                  [2] => Array
                                      (
                                          [is_closing] => 
                                          [is_singleton] => 
                                          [pointer] => 276
                                          [tag] => div
                                          [id] => Array
                                              (
                                                  [0] => somebody
                                              )
          
                                          [0] => Array
                                              (
                                                  [0] => Array
                                                      (
                                                          [tag] => __TEXT
                                                          [0] =>         Привет!
          
                                                      )
          
                                                  [1] => Array
                                                      (
                                                          [is_closing] => 1
                                                          [is_singleton] => 
                                                          [pointer] => 376
                                                          [tag] => div
                                                      )
          
                                              )
          
                                      )
          
                                  [3] => Array
                                      (
                                          [tag] => div
                                          [is_closing] => 1
                                      )
          
                              )
          
                      )
          
                  [1] => Array
                      (
                          [is_closing] => 1
                          [is_singleton] => 
                          [pointer] => 387
                          [tag] => body
                      )
          
              )
          
          )
          
          [2] => Array
          (
          [is_closing] => 1
          [is_singleton] => 
          [pointer] => 396
          [tag] => html
          )
          
          )
          
          )
          
          )
          

          Наверное, это правильный результат выполнение. Наверное.

          Тесты на необязательные теги

          После того, как разобрались с основным механизмом исправления ошибок в парсере, давайте поговорим про дополнительный. Для этого я возьму такой код:

          Код

          и можно опустить только если за ним следует пробельный символ. Но так как в нашем парсере пробельные символы опускаются, мы условимся, что в теге не допускается наличие любых других тегов. С
          37547 TEE Electric Powered Rail Car Train Functions (Abbreviated)
          Function Control Unit Central Station
          Headlights
          Interior Lights
          Electric locomotive operating sounds
          Engineer’s cab lighting
          Station Announcements - Swiss

        Давайте посмотрим на результат:

        Результат

        [0] => Array
        (
        [tag] => !DOCTYPE
        [html] => 1
        [is_singleton] => 1
        )
        
        [1] => Array
        (
        [tag] => html
        [is_singleton] => 
        [is_closing] => 
        [0] => Array
        (
        [0] => Array
        (
        [tag] => head
        [is_singleton] => 
        [is_closing] => 
        [0] => Array
        (
        [0] => Array
        (
        [tag] => head
        [is_singleton] => 
        [is_closing] => 1
        )
        
        )
        
        )
        
        [1] => Array
        (
        [tag] => body
        [is_singleton] => 
        [is_closing] => 
        [0] => Array
        (
        [0] => Array
        (
        [is_closing] => 
        [is_singleton] => 
        [pointer] => 7
        [tag] => table
        [0] => Array
        (
            [0] => Array
                (
                    [is_closing] => 
                    [is_singleton] => 
                    [pointer] => 18
                    [tag] => caption
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [tag] => __TEXT
                                    [0] => 37547 TEE Electric Powered Rail Car Train Functions (Abbreviated)
                                )
        
                            [1] => Array
                                (
                                    [tag] => caption
                                    [is_closing] => 1
                                )
        
                        )
        
                )
        
            [1] => Array
                (
                    [is_closing] => 
                    [is_singleton] => 
                    [pointer] => 95
                    [tag] => colgroup
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 1
                                    [pointer] => 100
                                    [tag] => col
                                )
        
                            [1] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 1
                                    [pointer] => 105
                                    [tag] => col
                                )
        
                            [2] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 1
                                    [pointer] => 110
                                    [tag] => col
                                )
        
                            [3] => Array
                                (
                                    [tag] => colgroup
                                    [is_closing] => 1
                                )
        
                        )
        
                )
        
            [2] => Array
                (
                    [is_closing] => 
                    [is_singleton] => 
                    [pointer] => 119
                    [tag] => thead
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 125
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 133
                                                    [tag] => th
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Function
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => th
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 149
                                                    [tag] => th
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Control Unit
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => th
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 169
                                                    [tag] => th
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Central Station
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => th
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [1] => Array
                                (
                                    [tag] => thead
                                    [is_closing] => 1
                                )
        
                        )
        
                )
        
            [3] => Array
                (
                    [is_closing] => 
                    [is_singleton] => 
                    [pointer] => 193
                    [tag] => tbody
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 199
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 207
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Headlights
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 225
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 236
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [1] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 245
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 253
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Interior Lights
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 276
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 287
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [2] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 296
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 304
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Electric locomotive operating sounds
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 348
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 359
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [3] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 368
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 376
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Engineer’s cab lighting
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 409
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 417
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [4] => Array
                                (
                                    [is_closing] => 
                                    [is_singleton] => 
                                    [pointer] => 426
                                    [tag] => tr
                                    [0] => Array
                                        (
                                            [0] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 434
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => Station Announcements - Swiss
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [1] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 471
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [2] => Array
                                                (
                                                    [is_closing] => 
                                                    [is_singleton] => 
                                                    [pointer] => 479
                                                    [tag] => td
                                                    [0] => Array
                                                        (
                                                            [0] => Array
                                                                (
                                                                    [tag] => __TEXT
                                                                    [0] => 
                                                                )
        
                                                            [1] => Array
                                                                (
                                                                    [tag] => td
                                                                    [is_closing] => 1
                                                                )
        
                                                        )
        
                                                )
        
                                            [3] => Array
                                                (
                                                    [tag] => tr
                                                    [is_closing] => 1
                                                )
        
                                        )
        
                                )
        
                            [5] => Array
                                (
                                    [tag] => tbody
                                    [is_closing] => 1
                                )
        
                        )
        
                )
        
            [4] => Array
                (
                    [is_closing] => 1
                    [is_singleton] => 
                    [pointer] => 492
                    [tag] => table
                )
        
        )
        
        )
        
        [1] => Array
        (
        [tag] => body
        [is_closing] => 1
        )
        
        )
        
        )
        
        [2] => Array
        (
        [tag] => html
        [is_closing] => 1
        )
        
        )
        
        )
        
        )
        

        Добавились как и закрывающие теги, вроде

        или , так и обязательные теги , и .

        Тесты производительности

        Внимание! Все тесты производительности будут происходить с заранее скачанными документами.

        Что ж, раз с этим разобрались, давайте приступать. Для теста возьмем мою первую публикацию, «Как я html-парсер на php писал, и что из этого вышло». Будем искать название публикации, а также показатель плюсиков. Начнем сначала со второго.

        Для того, чтобы найти этот показатель, я буду использовать такой код:

        $some->find('.voting-wjt__counter')->children(0)->children(0)->plainText();
        

        Такой код выводит результат выполнения в браузере за ~660ms.

        Теперь давайте найдем название публикации. Оно находится почти в самом верху, так что это будет быстрее. Для поиска я буду использовать такой код:

        $some->find('.post__title-text')->children(0)->plainText();
        

        Такой текст находится тоже за ~660ms.

        Как подключить PHP из другой директории

        Теперь изменим условия. Переместим файл `1.php` в папку с названием `test`, которую создадим в директории с файлом `index.php`.

        Поскольку подключаемый файл находится в другой директории, для его подключения следует указать сначала папку, в которой он находится, а потом и сам файл. 

        Далее изменим код в `index.php`.

        include 'test/1.php';
        ?>

        Строка, что мы указываем после инструкции `include`, это путь к файлу. Он может быть относительный (к файлу, где используется подключение) и абсолютным (от корня файловой системы). В приложениях рекомендуются использовать относительные пути.

        Между папками, файлами и другими папками в пути устанавливаются разделители. Универсальный разделитель для различных операционных систем – `/`. 

        Если в папке `test` у нас была бы еще папка `lot`, в которой лежал файл `1.php`, то относительный путь выглядел бы так: 'test/lot/1.php'.

        С путями немного разобрались – возвращаемся к инструкциям. Произведем изменения в файлах. Файл "index.php":

        
        html lang="ru">
        head>
            meta charset="UTF-8">
            title>Document/title>
        /head>
        body>
                    $say = 'Hello world!';
                include 'test/1.php';
        echo $test;
                echo "

        End/p>
        "; ?> /body> /html>

        Файл `1.php`:

        echo "

        {$say}

        "
        ; $test = 'TEst connect';

        Посмотрим на изменение в выводе:

        Как работает подключение кода PHP

        Интерпретатор php «читает» код сверху вниз и слева направо, как мы читаем книги на русском языке. На исполнение от сервера ему указывается файл "index.php", а значит, чтение начинается с него. Дойдя до строчки с `include 'test/1.php'`, интерпретатор пытается найти и исполнить это файл так, как будто он является частью "index.php". 

        Перед подключением и исполнением файла "1.php" уже существует переменная `$say`, в которой содержится 'Hello world!'. При выполнении файла "1.php", содержимое этой переменной выводится на экран и создается переменная `$test`, которая в свою очередь и выводится на экран в файле `index.php`.

        Если описанное выше непонятно, советую немного поиграться с файлами `1.php` и `index.php` создавая и выводя в них переменные.

        Различия `include`, `include_once`, `require`, `require_once`

        Переименуем файл "1.php"в файл "2.php" и обратимся к "index.php":

        В итоге получаем ошибку. Но обратите внимание на то, что после вывода ошибки код PHP все равно продолжил выполнение и вывел `End`. Заменим `include` на `require` и запустим на выполнение.

        В итоге видим похожие ошибки, но не видим вывода `End` в конце: после ошибки код php прекратил свою работу.

        Разница между `include` и `require` заключается в том, что при подключении файла PHP первая инструкция позволяет продолжить выполнения скрипта, если не найден файл, а `require` завершает его, выводя фатальную ошибку.

        Теперь рассмотрим отличие инструкций `require` и `require_once`. Внесем небольшие правки в наши файлы. Вот новый "index.php":

        
        html lang="ru">
        head>
            meta charset="UTF-8">
            title>Document/title>
        /head>
        body>
                    $say = 'Hello world!';
                require 'test/2.php';
                require 'test/2.php';
                require 'test/2.php';
                require 'test/2.php';
                echo "

        End/p>
        "; ?> /body> /html>

        И файл "2.php":

        echo "

        {$say}

        "
        ;

        Запускаем:

        Как видно на скриншоте, с помощью `require` мы успешно подключили файл несколько раз. Снова внесем изменение в файлы. Новый файл "index.php":

        
        html lang="ru">
        head>
            meta charset="UTF-8">
            title>Document/title>
        /head>
        body>
                    require 'test/2.php';
                require 'test/2.php';
                require 'test/2.php';
                require 'test/2.php';
                echo "p>End/p>";
            ?>
        /body>
        /html>

        И новый файл "2.php" — на этот раз объявим там функцию:

        echo '

        Im included

        '
        ; function sayHello($say) { echo "

        {$say}

        "
        ; }

        Результат выполнения:

        Второе подключение файла "2.php" приводит к ошибке, как раз потому что в этом файле происходит объявление функции. А в PHP-скрипте двух одинаковых функций быть не должно.

        Теперь заменим все `require` на `require_once` и запустим снова:

        Ура, работает! Но обратим внимание на то, что файл подключился только один раз.

        Теперь вновь переименуем файл `2.php` в `1.php` и запустим "index.php".

        `Require_once`, так же как и `require` завершает выполнение скрипта, если не найден файл указанный для подключения. Заменим `require_once` на `include_once`:

        Ошибок стало больше, но код по-прежнему отработал до конца: end в конце картинки это подтверждает. Внесем правки в "index.php":

        
        html lang="ru">
        head>
            meta charset="UTF-8">
            title>Document/title>
        /head>
        body>
                    include_once 'test/1.php';
                include_once 'test/1.php';
                include_once 'test/1.php';
                include_once 'test/1.php';
                echo "

        End/p>
        "; ?> /body> /html>

        Запустим код:

        Базовый метод настройки редиректа PHP

        Большинство руководств скажут вам, что для создания PHP редиректа вы можете просто использовать функцию header() в верхней части ваших страниц. В этом случае вы используете функцию, чтобы отправлять новый URL, например:

        header('Location: '.$newURL.php);

        Поскольку эта функция должна предшествовать отправке в браузеры ваших пользователей какого-либо HTML или текста, её нужно поместить прямо вверху страницы. Это означает, что она должна стоять перед объявлением , перед кодом Java и PHP. Затем она отправит пользователей на новый URL-адрес.

        Всё предельно ясно, однако функция header() не так проста, как кажется. Итак, давайте посмотрим, как правильно использовать эту функцию.

        Die() и Exit()

        Во-первых, вы должны использовать модификатор die() или exit() каждый раз, когда используете редирект. Если вкратце, то проблема в том, что сканеры и роботы могут игнорировать заголовки, и поэтому страница, с которой, как вы думали, вы перенаправляете, полностью доступна для них. Другими словами, если вы используете редирект путём отправки нового заголовка, чтобы защитить определённую страницу, это не даёт вам никакой защиты.

        Вот почему вам нужно предотвратить обработку остальной части страницы. Для этого добавьте die() или exit() после перенаправления:

        header("Location: .$newURL.php");
        die();
        

        Относительные и абсолютные URL

        Теперь поговорим об относительных и абсолютных ссылках в переадресации. RFC 7231 позволяет использовать оба типа, однако, если вы используете относительные редиректы следует быть особенно осторожным. Дело в том, что некоторые конструкторы сайтов объединяют и переименовывают страницы PHP. Это означает, что если вы работаете над своим PHP через конструктор сайтов, все ваши редиректы могут просто поломаться.

        К сожалению, на данный момент нет реального способа обойти эту проблему, кроме как внимательно следить за тем, на что указывают ваши редиректы.

        Коды статусов

        Третья проблема стандартных перенаправлений PHP заключается в том, что оператор PHP «location» по умолчанию возвращает код HTTP 302 (англ.). Вам нужно запретить ему это делать, поскольку многие браузеры реализуют этот код совсем не так, как он должен функционировать на само деле: они, по сути, используют команду GET вместо выполнения «настоящего» редиректа.

        Поэтому при создании редиректов на PHP рекомендуется указывать возвращаемый код статуса. К сожалению, нет единого мнения, какой код стоит указывать. HTTP 301 указывает на постоянное перенаправление, что может вызвать проблемы при восстановлении исходной страницы. HTTP 303, к сожалению, воспринимается многими браузерами как «другой» и может вызвать проблемы с индексированием вашей страницы в поисковых системах.

        Пока этот вопрос остаётся открытым, используйте HTTP 303.

        Проверка документации

        Помимо основных мер предосторожности, описанных выше, вам следует потратить некоторое время на то, чтобы прочитать документацию по использованию редиректов PHP. Ознакомьтесь с руководством по PHP и документацию W3C (англ.), чтобы быть уверенными в своих действиях и выполнить все настройки в соответствии с последними стандартами.

        Кроме прочтения рекомендованных материалов, также не забудьте защитить свой сайт от распространённых уязвимостей. Если вы уже используете редиректы PHP, скорее всего, вам придётся провести аудит безопасности вашего сайта.

        Логотип и Заголовок "Список Уязвимостей" на Сайте PrivacyCanada

        Другие методы

        Учитывая все эти проблемы, вы, вероятно, задаётесь вопросом, зачем вообще использовать редиректы PHP. И это вполне логический вопрос. Несмотря на то, что PHP редирект обычно выполняется быстрее, чем другие типы перенаправлений, и, следовательно, является инструментом повышения скорости сайта, существуют и другие варианты.

        Есть два основных подхода. Вы можете использовать элемент HTML для перенаправления из HTML-части вашей страницы или же использовать JavaScript. Первый подход — будет выглядеть так:

        Второй подход — с использованием JavaScript — выглядит немного элегантней и более профессионально:

        window.location.replace("http://newpage.php/");

        Оба варианта будут выполняться немного медленнее, чем перенаправление header(), однако являются более гибкими.