Skip to content

Commit 8bf35cd

Browse files
committed
Refactored the Selector class
1 parent c6f6712 commit 8bf35cd

File tree

6 files changed

+135
-107
lines changed

6 files changed

+135
-107
lines changed

src/PHPHtmlParser/Dom/AbstractNode.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
use PHPHtmlParser\Exceptions\CircularException;
55
use PHPHtmlParser\Exceptions\ParentNotFoundException;
66
use PHPHtmlParser\Exceptions\ChildNotFoundException;
7-
use PHPHtmlParser\Selector;
7+
use PHPHtmlParser\Selector\Selector;
8+
use PHPHtmlParser\Selector\Parser as SelectorParser;
89
use stringEncode\Encode;
910
use PHPHtmlParser\Finder;
1011

@@ -430,7 +431,7 @@ public function ancestorByTag(string $tag): AbstractNode
430431
*/
431432
public function find(string $selector, int $nth = null)
432433
{
433-
$selector = new Selector($selector);
434+
$selector = new Selector($selector, new SelectorParser());
434435
$nodes = $selector->find($this);
435436

436437
if ( ! is_null($nth)) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
namespace PHPHtmlParser\Selector;
3+
4+
/**
5+
* This is the parser for the selctor.
6+
*
7+
*
8+
*/
9+
class Parser implements ParserInterface
10+
{
11+
12+
/**
13+
* Pattern of CSS selectors, modified from 'mootools'
14+
*
15+
* @var string
16+
*/
17+
protected $pattern = "/([\w\-:\*>]*)(?:\#([\w\-]+)|\.([\w\-]+))?(?:\[@?(!?[\w\-:]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
18+
19+
/**
20+
* Parses the selector string
21+
*
22+
* @param string $selector
23+
*/
24+
public function parseSelectorString(string $selector): array
25+
{
26+
$selectors = [];
27+
28+
$matches = [];
29+
preg_match_all($this->pattern, trim($selector).' ', $matches, PREG_SET_ORDER);
30+
31+
// skip tbody
32+
$result = [];
33+
foreach ($matches as $match) {
34+
// default values
35+
$tag = strtolower(trim($match[1]));
36+
$operator = '=';
37+
$key = null;
38+
$value = null;
39+
$noKey = false;
40+
$alterNext = false;
41+
42+
// check for elements that alter the behavior of the next element
43+
if ($tag == '>') {
44+
$alterNext = true;
45+
}
46+
47+
// check for id selector
48+
if ( ! empty($match[2])) {
49+
$key = 'id';
50+
$value = $match[2];
51+
}
52+
53+
// check for class selector
54+
if ( ! empty($match[3])) {
55+
$key = 'class';
56+
$value = $match[3];
57+
}
58+
59+
// and final attribute selector
60+
if ( ! empty($match[4])) {
61+
$key = strtolower($match[4]);
62+
}
63+
if ( ! empty($match[5])) {
64+
$operator = $match[5];
65+
}
66+
if ( ! empty($match[6])) {
67+
$value = $match[6];
68+
}
69+
70+
// check for elements that do not have a specified attribute
71+
if (isset($key[0]) && $key[0] == '!') {
72+
$key = substr($key, 1);
73+
$noKey = true;
74+
}
75+
76+
$result[] = [
77+
'tag' => $tag,
78+
'key' => $key,
79+
'value' => $value,
80+
'operator' => $operator,
81+
'noKey' => $noKey,
82+
'alterNext' => $alterNext,
83+
];
84+
if (trim($match[7]) == ',') {
85+
$selectors[] = $result;
86+
$result = [];
87+
}
88+
}
89+
90+
// save last results
91+
if (count($result) > 0) {
92+
$selectors[] = $result;
93+
}
94+
95+
return $selectors;
96+
}
97+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
namespace PHPHtmlParser\Selector;
3+
4+
interface ParserInterface
5+
{
6+
public function parseSelectorString(string $selector): array;
7+
}

src/PHPHtmlParser/Selector.php renamed to src/PHPHtmlParser/Selector/Selector.php

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
<?php
2-
namespace PHPHtmlParser;
2+
namespace PHPHtmlParser\Selector;
33

44
use PHPHtmlParser\Dom\AbstractNode;
55
use PHPHtmlParser\Dom\Collection;
66
use PHPHtmlParser\Dom\InnerNode;
77
use PHPHtmlParser\Dom\LeafNode;
88
use PHPHtmlParser\Exceptions\ChildNotFoundException;
9-
use Countable;
109

1110
/**
1211
* Class Selector
@@ -17,22 +16,18 @@ class Selector
1716
{
1817

1918
/**
20-
* Pattern of CSS selectors, modified from 'mootools'
21-
*
22-
* @var string
19+
* @var array
2320
*/
24-
protected $pattern = "/([\w\-:\*>]*)(?:\#([\w\-]+)|\.([\w\-]+))?(?:\[@?(!?[\w\-:]+)(?:([!*^$]?=)[\"']?(.*?)[\"']?)?\])?([\/, ]+)/is";
25-
2621
protected $selectors = [];
2722

2823
/**
2924
* Constructs with the selector string
3025
*
3126
* @param string $selector
3227
*/
33-
public function __construct($selector)
28+
public function __construct(string $selector, ParserInterface $parser)
3429
{
35-
$this->parseSelectorString($selector);
30+
$this->selectors = $parser->parseSelectorString($selector);
3631
}
3732

3833
/**
@@ -81,80 +76,6 @@ public function find(AbstractNode $node): Collection
8176
return $results;
8277
}
8378

84-
/**
85-
* Parses the selector string
86-
*
87-
* @param string $selector
88-
*/
89-
protected function parseSelectorString(string $selector): void
90-
{
91-
$matches = [];
92-
preg_match_all($this->pattern, trim($selector).' ', $matches, PREG_SET_ORDER);
93-
94-
// skip tbody
95-
$result = [];
96-
foreach ($matches as $match) {
97-
// default values
98-
$tag = strtolower(trim($match[1]));
99-
$operator = '=';
100-
$key = null;
101-
$value = null;
102-
$noKey = false;
103-
$alterNext = false;
104-
105-
// check for elements that alter the behavior of the next element
106-
if ($tag == '>') {
107-
$alterNext = true;
108-
}
109-
110-
// check for id selector
111-
if ( ! empty($match[2])) {
112-
$key = 'id';
113-
$value = $match[2];
114-
}
115-
116-
// check for class selector
117-
if ( ! empty($match[3])) {
118-
$key = 'class';
119-
$value = $match[3];
120-
}
121-
122-
// and final attribute selector
123-
if ( ! empty($match[4])) {
124-
$key = strtolower($match[4]);
125-
}
126-
if ( ! empty($match[5])) {
127-
$operator = $match[5];
128-
}
129-
if ( ! empty($match[6])) {
130-
$value = $match[6];
131-
}
132-
133-
// check for elements that do not have a specified attribute
134-
if (isset($key[0]) && $key[0] == '!') {
135-
$key = substr($key, 1);
136-
$noKey = true;
137-
}
138-
139-
$result[] = [
140-
'tag' => $tag,
141-
'key' => $key,
142-
'value' => $value,
143-
'operator' => $operator,
144-
'noKey' => $noKey,
145-
'alterNext' => $alterNext,
146-
];
147-
if (trim($match[7]) == ',') {
148-
$this->selectors[] = $result;
149-
$result = [];
150-
}
151-
}
152-
153-
// save last results
154-
if (count($result) > 0) {
155-
$this->selectors[] = $result;
156-
}
157-
}
15879

15980
/**
16081
* Attempts to find all children that match the rule

tests/CollectionTest.php

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
declare(strict_types=1);
33

44
use PHPUnit\Framework\TestCase;
5-
use PHPHtmlParser\Selector;
5+
use PHPHtmlParser\Selector\Selector;
6+
use PHPHtmlParser\Selector\Parser;
67
use PHPHtmlParser\Dom\HtmlNode;
78
use PHPHtmlParser\Dom\Tag;
89
use PHPHtmlParser\Dom\Collection;
@@ -21,7 +22,7 @@ public function testEach()
2122
$parent->addChild($child2);
2223
$child2->addChild($child3);
2324

24-
$selector = new Selector('a');
25+
$selector = new Selector('a', new Parser());
2526
$collection = $selector->find($root);
2627
$count = 0;
2728
$collection->each(function ($node) use (&$count) {
@@ -58,7 +59,7 @@ public function testCallMagic()
5859
$parent->addChild($child2);
5960
$child2->addChild($child3);
6061

61-
$selector = new Selector('div * a');
62+
$selector = new Selector('div * a', new Parser());
6263
$this->assertEquals($child3->id(), $selector->find($root)->id());
6364
}
6465

@@ -74,7 +75,7 @@ public function testGetMagic()
7475
$parent->addChild($child2);
7576
$child2->addChild($child3);
7677

77-
$selector = new Selector('div * a');
78+
$selector = new Selector('div * a', new Parser());
7879
$this->assertEquals($child3->innerHtml, $selector->find($root)->innerHtml);
7980
}
8081

@@ -99,7 +100,7 @@ public function testToStringMagic()
99100
$parent->addChild($child2);
100101
$child2->addChild($child3);
101102

102-
$selector = new Selector('div * a');
103+
$selector = new Selector('div * a', new Parser());
103104
$this->assertEquals((string)$child3, (string)$selector->find($root));
104105
}
105106

@@ -115,7 +116,7 @@ public function testToArray()
115116
$parent->addChild($child2);
116117
$child2->addChild($child3);
117118

118-
$selector = new Selector('a');
119+
$selector = new Selector('a', new Parser());
119120
$collection = $selector->find($root);
120121
$array = $collection->toArray();
121122
$lastA = end($array);

0 commit comments

Comments
 (0)