У меня есть эта строка:
<ul> <li id="1">Page 1</li> <li id="2">Page 2 <ul> <li id="3">Sub Page A</li> <li id="4">Sub Page B</li> <li id="5">Sub Page C <ul> <li id="6">Sub Sub Page I</li> </ul> </li> </ul> </li> <li id="7">Page 3 <ul> <li id="8">Sub Page D</li> </ul> </li> <li id="9">Page 4</li> </ul>
и я хочу взорвать каждую информацию с помощью PHP и сделать это так:
---------------------------------- | ID | ORDER | PARENT | CHILDREN | ---------------------------------- | 1 | 1 | 0 | 0 | | 2 | 2 | 0 | 3,4,5 | | 3 | 1 | 2 | 0 | | 4 | 2 | 2 | 0 | | 5 | 3 | 2 | 6 | | 6 | 1 | 5 | 0 | | 7 | 3 | 0 | 8 | | 8 | 1 | 7 | 0 | | 9 | 4 | 0 | 0 | ----------------------------------
Для получения дополнительной информации это то, что для меня означает этот список:
ID 1 является 1-й (1) и имеет 0 родителей и 0 детей,
ID 2 – 2-й (Страница 2) и имеет 0 идентификаторов родителей и детей 3,4,5,
ID 3 является 1-й (подстраница A) и имеет родительский идентификатор 2 и 0 детей,
ID 4 – 2-я (Субстраница B) и имеет родительский идентификатор 2 и 0 детей,
Идентификатор 5 – третий (подстраница C) и имеет родительский идентификатор 2 и идентификатор детей 6,
ID 6 – 1-я (под-страница I) и имеет родительский идентификатор 5 и 0 детей,
ID 7 – 3-й (Страница 3) и имеет 0 родителей и детей ID 8,
ID 8 – 1-я (Суб-страница I) и имеет родительский идентификатор 7 и 0 детей,
ID 9 – четвертый (Страница 4) и имеет 0 родителей и 0 детей.
Если это слишком сложно, может ли кто-нибудь сказать, как получить эту информацию из этой строки другим методом?
Это не «строка», это HTML. Вам нужно использовать парсер HTML, например DOMDocument или simple_html_dom .
См. Примеры по адресу http://htmlparsing.com/php.html.
Здесь вы можете разделить проблему. Единственное, что нужно было бы проанализировать HTML, это проще всего сделать с DOMDocument
и DOMXpath
здесь. Это выполняется некоторое сопоставление в контексте результата другого выражения / запроса xpath. Звучит, может быть, немного сложно, но это не так. В более упрощенном варианте вы можете найти это в предыдущем ответе, чтобы получить родительский элемент через xpath и все дочерние элементы .
В вашем случае это немного усложняет, некоторый псевдокод. Я добавил ярлык, потому что он делает вещи более заметными для демонстрационных целей:
foreach //li :: ID := string(./@id) ParentID := string(./ancestor::li[1]/@id) Label := normalize-space(./text()[1])
Как показано на рисунке, это возвращает только голые данные. У вас также есть Орден и дети. Обычно список детей не нужен (я все равно его здесь). То, что похоже на значение Order и значение Children, – это то, что они извлекаются из контекста.
Например, при перемещении n-лидера в личном порядке порядок каждого ребенка может быть пронумерован, если счетчик хранится на каждый ParentID.
Подобно Детям, как счетчик, эта ценность должна быть построена, итерации по списку. Только в самом конце доступно правильное значение для каждого списка.
Таким образом, эти два значения находятся в контексте, я создаю этот контекст в виде массива с ключом ParentID: $parents
. За каждый идентификатор он будет содержать две записи: 0 содержит счетчик для Order и 1, содержащий массив, чтобы содержать идентификаторы детей (если они есть).
Примечание. Технически это не совсем правильно. Заказ и дети должны быть выражены и в чистом xpath, но я просто не делал этого в этом примере, чтобы показать, как добавить свой собственный не-xpath-контекст, например, если вы хотите использовать другой порядок или обработку детей.
Достаточно с теорией. Учитывая стандартную настройку:
$doc = new DOMDocument(); $doc->loadHTML($html); $xp = new DOMXPath($doc);
Указанное отображение, вкл. его контекст может быть написан как анонимная функция:
$parents = []; $map = function (DOMElement $li) use ($xp, &$parents) { $id = (int)$xp->evaluate('string(./@id)', $li); $parentId = (int)$xp->evaluate('string(./ancestor::li[1]/@id)', $li); $label = $xp->evaluate('normalize-space(./text()[1])', $li); isset($parents[$parentId][0]) ? $parents[$parentId][0]++ : ($parents[$parentId][0] = 1); $order = $parents[$parentId][0]; $parents[$parentId][1][] = $id; isset($parents[$id][1]) || $parents[$id][1] = []; return array($id, $label, $order, $parentId, &$parents[$id][1]); };
Как вы можете видеть, сначала он содержит поиск значений, подобных псевдокоду, а во второй части – обработку значений контекста. Это просто инициализировать контекст для ID / ParentID, если он еще не существует.
Это сопоставление должно быть применено:
$result = []; foreach ($xp->query('//li') as $li) { list($id) = $array = $map($li); $result[$id] = $array; }
Который сделает $result
содержит список элементов и $parents
– данные контекста. В качестве ссылки используется значение «Дети», которое нужно сейчас взорвать, тогда ссылки можно удалить:
foreach ($parents as &$parent) { $parent[1] = implode(',', $parent[1]); } unset($parent, $parents);
Это затем приводит к $result
result, который может быть выведен:
echo '+----+----------------+-------+--------+----------+ | ID | LABEL | ORDER | PARENT | CHILDREN | +----+----------------+-------+--------+----------+ '; foreach ($result as $line) { vprintf("| %' 2d | %' -14s | %' 2d | %' 2d | %-8s |\n", $line); } echo '+----+----------------+-------+--------+----------+ ';
Что тогда выглядит:
+----+----------------+-------+--------+----------+ | ID | LABEL | ORDER | PARENT | CHILDREN | +----+----------------+-------+--------+----------+ | 1 | Page 1 | 1 | 0 | | | 2 | Page 2 | 2 | 0 | 3,4,5 | | 3 | Sub Page A | 1 | 2 | | | 4 | Sub Page B | 2 | 2 | | | 5 | Sub Page C | 3 | 2 | 6 | | 6 | Sub Sub Page I | 1 | 5 | | | 7 | Page 3 | 3 | 0 | 8 | | 8 | Sub Page D | 1 | 7 | | | 9 | Page 4 | 4 | 0 | | +----+----------------+-------+--------+----------+
Вы можете найти Демо онлайн здесь .
Я оставляю второй ответ, потому что на этот раз это демонстрирует, как это сделать с единственным отображением (в псевдокоде):
foreach //li :: ID := string(./@id) ParentID := string(./ancestor::li[1]/@id) Label := normalize-space(./text()[1]) Order := count(./preceding-sibling::li)+1 Children := implode(",", ./ul/li/@id)
Поскольку это может быть сделано для каждого узла li
независимо от того, в каком порядке, это может быть идеальным совпадением для Iterator
, здесь текущая функция:
public function current() { return [ 'ID' => $this->evaluate('number(./@id)'), 'label' => $this->evaluate('normalize-space(./text()[1])'), 'order' => $this->evaluate('count(./preceding-sibling::li)+1'), 'parentID' => $this->evaluate('number(concat("0", ./ancestor::li[1]/@id))'), 'children' => $this->implodeNodes(',', './ul/li/@id'), ]; }
Полный пример ( демонстрационный ) вывод и код:
+----+----------------+-------+--------+----------+ | ID | LABEL | ORDER | PARENT | CHILDREN | +----+----------------+-------+--------+----------+ | 1 | Page 1 | 1 | 0 | | | 2 | Page 2 | 2 | 0 | 3,4,5 | | 3 | Sub Page A | 1 | 2 | | | 4 | Sub Page B | 2 | 2 | | | 5 | Sub Page C | 3 | 2 | 6 | | 6 | Sub Sub Page I | 1 | 5 | | | 7 | Page 3 | 3 | 0 | 8 | | 8 | Sub Page D | 1 | 7 | | | 9 | Page 4 | 4 | 0 | | +----+----------------+-------+--------+----------+ class HtmlListIterator extends IteratorIterator { private $xpath; public function __construct($html) { $doc = new DOMDocument(); $doc->loadHTML($html); $this->xpath = new DOMXPath($doc); parent::__construct($this->xpath->query('//li')); } private function evaluate($expression) { return $this->xpath->evaluate($expression, parent::current()); } private function implodeNodes($glue, $expression) { return implode( $glue, array_map(function ($a) { return $a->nodeValue; }, iterator_to_array($this->evaluate($expression, parent::current()))) ); } public function current() { return [ 'ID' => $this->evaluate('number(./@id)'), 'label' => $this->evaluate('normalize-space(./text()[1])'), 'order' => $this->evaluate('count(./preceding-sibling::li)+1'), 'parentID' => $this->evaluate('number(concat("0", ./ancestor::li[1]/@id))'), 'children' => $this->implodeNodes(',', './ul/li/@id'), ]; } } print_result(new HtmlListIterator($html)); function print_result($result) { echo '+----+----------------+-------+--------+----------+ | ID | LABEL | ORDER | PARENT | CHILDREN | +----+----------------+-------+--------+----------+ '; foreach ($result as $line) { vprintf("| %' 2d | %' -14s | %' 2d | %' 2d | %-8s |\n", $line); } echo '+----+----------------+-------+--------+----------+ '; }