Gallery Custom Links - Version 1.2.7

Version Description

  • Add: Remove warnings for PHP 7.4.
  • Update: New versions of DiDom and the Simple HTML DOM Parser.
  • Info: If you like the plugin, your reviews are welcome here :) Thank you!
Download this release

Release Info

Developer TigrouMeow
Plugin Icon 128x128 Gallery Custom Links
Version 1.2.7
Comparing to
See all releases

Code changes from version 1.2.6 to 1.2.7

Files changed (37) hide show
  1. common/img/kinsta.png +0 -0
  2. common/img/stackpath.png +0 -0
  3. composer.lock +10 -11
  4. gallery-custom-links.php +2 -2
  5. mgcl_admin.php +1 -1
  6. mgcl_core.php +2 -3
  7. mgcl_linker.php +3 -2
  8. readme.txt +7 -3
  9. vendor/composer/ClassLoader.php +1 -1
  10. vendor/composer/installed.json +12 -13
  11. vendor/imangazaliev/didom/.gitignore +0 -1
  12. vendor/imangazaliev/didom/.travis.yml +7 -6
  13. vendor/imangazaliev/didom/README-RU.md +1 -1
  14. vendor/imangazaliev/didom/README.md +1 -1
  15. vendor/imangazaliev/didom/composer.json +7 -3
  16. vendor/imangazaliev/didom/composer.lock +1049 -0
  17. vendor/imangazaliev/didom/phpunit.xml +12 -11
  18. vendor/imangazaliev/didom/src/DiDom/ClassAttribute.php +22 -24
  19. vendor/imangazaliev/didom/src/DiDom/Document.php +116 -101
  20. vendor/imangazaliev/didom/src/DiDom/DocumentFragment.php +34 -0
  21. vendor/imangazaliev/didom/src/DiDom/Element.php +63 -1139
  22. vendor/imangazaliev/didom/src/DiDom/Node.php +1193 -0
  23. vendor/imangazaliev/didom/src/DiDom/Query.php +25 -22
  24. vendor/imangazaliev/didom/src/DiDom/StyleAttribute.php +30 -32
  25. vendor/imangazaliev/didom/tests/DiDom/ClassAttributeTest.php +0 -383
  26. vendor/imangazaliev/didom/tests/DiDom/DocumentTest.php +0 -772
  27. vendor/imangazaliev/didom/tests/DiDom/ElementTest.php +0 -2094
  28. vendor/imangazaliev/didom/tests/DiDom/QueryTest.php +0 -425
  29. vendor/imangazaliev/didom/tests/DiDom/SelectorTest.php +0 -327
  30. vendor/imangazaliev/didom/tests/DiDom/StyleAttributeTest.php +0 -515
  31. vendor/imangazaliev/didom/tests/TestCase.php +0 -40
  32. vendor/imangazaliev/didom/tests/bootstrap.php +0 -7
  33. vendor/imangazaliev/didom/tests/fixtures/books.xml +0 -120
  34. vendor/imangazaliev/didom/tests/fixtures/menu.html +0 -15
  35. vendor/imangazaliev/didom/tests/fixtures/posts.html +0 -25
  36. vendor/kub-at/php-simple-html-dom-parser/README.md +2 -2
  37. vendor/kub-at/php-simple-html-dom-parser/src/KubAT/PhpSimple/lib/simple_html_dom.php +282 -737
common/img/kinsta.png CHANGED
Binary file
common/img/stackpath.png CHANGED
Binary file
composer.lock CHANGED
@@ -8,16 +8,16 @@
8
  "packages": [
9
  {
10
  "name": "imangazaliev/didom",
11
- "version": "1.14.1",
12
  "source": {
13
  "type": "git",
14
  "url": "https://github.com/Imangazaliev/DiDOM.git",
15
- "reference": "a8389cc26897f6bb8843363b4ea16e7ef16fb3c3"
16
  },
17
  "dist": {
18
  "type": "zip",
19
- "url": "https://api.github.com/repos/Imangazaliev/DiDOM/zipball/a8389cc26897f6bb8843363b4ea16e7ef16fb3c3",
20
- "reference": "a8389cc26897f6bb8843363b4ea16e7ef16fb3c3",
21
  "shasum": ""
22
  },
23
  "require": {
@@ -26,7 +26,6 @@
26
  "php": ">=5.4"
27
  },
28
  "require-dev": {
29
- "friendsofphp/php-cs-fixer": "2.9",
30
  "phpunit/phpunit": "^4.8"
31
  },
32
  "type": "library",
@@ -53,20 +52,20 @@
53
  "parser",
54
  "xml"
55
  ],
56
- "time": "2019-01-17T11:01:36+00:00"
57
  },
58
  {
59
  "name": "kub-at/php-simple-html-dom-parser",
60
- "version": "1.8.1",
61
  "source": {
62
  "type": "git",
63
  "url": "https://github.com/Kub-AT/php-simple-html-dom-parser.git",
64
- "reference": "6db1e01db320040024cd1f74b0e1483aa2670720"
65
  },
66
  "dist": {
67
  "type": "zip",
68
- "url": "https://api.github.com/repos/Kub-AT/php-simple-html-dom-parser/zipball/6db1e01db320040024cd1f74b0e1483aa2670720",
69
- "reference": "6db1e01db320040024cd1f74b0e1483aa2670720",
70
  "shasum": ""
71
  },
72
  "require": {
@@ -99,7 +98,7 @@
99
  "dom",
100
  "html"
101
  ],
102
- "time": "2019-03-05T14:12:22+00:00"
103
  }
104
  ],
105
  "packages-dev": [],
8
  "packages": [
9
  {
10
  "name": "imangazaliev/didom",
11
+ "version": "1.16",
12
  "source": {
13
  "type": "git",
14
  "url": "https://github.com/Imangazaliev/DiDOM.git",
15
+ "reference": "c2a8bd08679d2eec856782277267a48553322c7e"
16
  },
17
  "dist": {
18
  "type": "zip",
19
+ "url": "https://api.github.com/repos/Imangazaliev/DiDOM/zipball/c2a8bd08679d2eec856782277267a48553322c7e",
20
+ "reference": "c2a8bd08679d2eec856782277267a48553322c7e",
21
  "shasum": ""
22
  },
23
  "require": {
26
  "php": ">=5.4"
27
  },
28
  "require-dev": {
 
29
  "phpunit/phpunit": "^4.8"
30
  },
31
  "type": "library",
52
  "parser",
53
  "xml"
54
  ],
55
+ "time": "2020-05-12T21:04:30+00:00"
56
  },
57
  {
58
  "name": "kub-at/php-simple-html-dom-parser",
59
+ "version": "1.9.1",
60
  "source": {
61
  "type": "git",
62
  "url": "https://github.com/Kub-AT/php-simple-html-dom-parser.git",
63
+ "reference": "ff22f98bfd9235115c128059076f3eb740d66913"
64
  },
65
  "dist": {
66
  "type": "zip",
67
+ "url": "https://api.github.com/repos/Kub-AT/php-simple-html-dom-parser/zipball/ff22f98bfd9235115c128059076f3eb740d66913",
68
+ "reference": "ff22f98bfd9235115c128059076f3eb740d66913",
69
  "shasum": ""
70
  },
71
  "require": {
98
  "dom",
99
  "html"
100
  ],
101
+ "time": "2019-10-25T12:34:43+00:00"
102
  }
103
  ],
104
  "packages-dev": [],
gallery-custom-links.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Gallery Custom Links
4
  Plugin URI: https://meowapps.com
5
  Description: Gallery Custom Links allows you to link images from galleries to a specified URL. Tested with WordPress Gallery, Gutenberg, the Meow Gallery and others.
6
- Version: 1.2.6
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: gallery-custom-links
@@ -23,7 +23,7 @@ if ( class_exists( 'Meow_Gallery_Custom_Links' ) ) {
23
  }
24
 
25
  global $mgcl_version;
26
- $mgcl_version = '1.2.6';
27
 
28
  include "mgcl_admin.php";
29
  $mgcl_admin = new Meow_Gallery_Custom_Links_Admin( 'mgcl', __FILE__, 'gallery-custom-links' );
3
  Plugin Name: Gallery Custom Links
4
  Plugin URI: https://meowapps.com
5
  Description: Gallery Custom Links allows you to link images from galleries to a specified URL. Tested with WordPress Gallery, Gutenberg, the Meow Gallery and others.
6
+ Version: 1.2.7
7
  Author: Jordy Meow
8
  Author URI: https://meowapps.com
9
  Text Domain: gallery-custom-links
23
  }
24
 
25
  global $mgcl_version;
26
+ $mgcl_version = '1.2.7';
27
 
28
  include "mgcl_admin.php";
29
  $mgcl_admin = new Meow_Gallery_Custom_Links_Admin( 'mgcl', __FILE__, 'gallery-custom-links' );
mgcl_admin.php CHANGED
@@ -174,7 +174,7 @@ class Meow_Gallery_Custom_Links_Admin extends MeowApps_Admin { // extends MeowAp
174
 
175
  function admin_parsing_engine_callback( $args ) {
176
  $layouts = array(
177
- //'None' => array( 'name' => __( 'None', 'gallery-custom-links' ), 'desc' => "" ),
178
  'HtmlDomParser' => array( 'name' => __( 'HtmlDomParser', 'gallery-custom-links' ), 'desc' => "" ),
179
  'DiDom' => array( 'name' => __( 'DiDom', 'gallery-custom-links' ), 'desc' => "" )
180
  );
174
 
175
  function admin_parsing_engine_callback( $args ) {
176
  $layouts = array(
177
+ 'None' => array( 'name' => __( 'None (Not Supported Yet)', 'gallery-custom-links' ), 'desc' => "" ),
178
  'HtmlDomParser' => array( 'name' => __( 'HtmlDomParser', 'gallery-custom-links' ), 'desc' => "" ),
179
  'DiDom' => array( 'name' => __( 'DiDom', 'gallery-custom-links' ), 'desc' => "" )
180
  );
mgcl_core.php CHANGED
@@ -152,7 +152,6 @@ class Meow_Gallery_Custom_Links
152
  $this->isEnabled = apply_filters( 'gallery_custom_links_enabled', true );
153
  if ( !$this->isEnabled || !isset( $buffer ) || trim( $buffer ) === '' )
154
  return $buffer;
155
-
156
  if ( $this->parsingEngine === 'HtmlDomParser' ) {
157
  $html = new KubAT\PhpSimple\HtmlDomParser();
158
  $html = $html->str_get_html( $buffer, true, true, DEFAULT_TARGET_CHARSET, false );
@@ -161,9 +160,9 @@ class Meow_Gallery_Custom_Links
161
  $html = new DiDom\Document();
162
  $html->preserveWhiteSpace();
163
  if ( defined( 'LIBXML_HTML_NOIMPLIED' ) && defined( 'LIBXML_HTML_NODEFDTD' ) )
164
- $html->loadHtml( $buffer, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
165
  else
166
- $html->loadHtml( $buffer, 0 );
167
  }
168
 
169
  if ( empty( $html ) || is_bool( $html ) ) {
152
  $this->isEnabled = apply_filters( 'gallery_custom_links_enabled', true );
153
  if ( !$this->isEnabled || !isset( $buffer ) || trim( $buffer ) === '' )
154
  return $buffer;
 
155
  if ( $this->parsingEngine === 'HtmlDomParser' ) {
156
  $html = new KubAT\PhpSimple\HtmlDomParser();
157
  $html = $html->str_get_html( $buffer, true, true, DEFAULT_TARGET_CHARSET, false );
160
  $html = new DiDom\Document();
161
  $html->preserveWhiteSpace();
162
  if ( defined( 'LIBXML_HTML_NOIMPLIED' ) && defined( 'LIBXML_HTML_NODEFDTD' ) )
163
+ $html->loadHtml( (string)$buffer, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
164
  else
165
+ $html->loadHtml( (string)$buffer, 0 );
166
  }
167
 
168
  if ( empty( $html ) || is_bool( $html ) ) {
mgcl_linker.php CHANGED
@@ -10,7 +10,8 @@ class Meow_Gallery_Custom_Links_Linker {
10
  // XXXX: Custom code with $aria, Christoph Letmaier, 14.01.2020
11
  function linker( $element, $parent, $mediaId, $url, $rel, $aria, $target ) {
12
  // Let's look for the closest link tag enclosing the image
13
- $title = get_the_title( $mediaId );
 
14
  $potentialLinkNode = $parent;
15
  $maxDepth = 5;
16
  do {
@@ -46,7 +47,7 @@ class Meow_Gallery_Custom_Links_Linker {
46
  if ( !empty( $rel ) )
47
  $potentialLinkNode->attr( 'rel', $rel );
48
  if ( !empty( $aria ) )
49
- $potentialLinkNode->attr{'aria-label'} = $aria;
50
  }
51
  return true;
52
  }
10
  // XXXX: Custom code with $aria, Christoph Letmaier, 14.01.2020
11
  function linker( $element, $parent, $mediaId, $url, $rel, $aria, $target ) {
12
  // Let's look for the closest link tag enclosing the image
13
+ $media = get_post( $mediaId );
14
+ $title = the_title_attribute( array( 'echo' => false, 'post' => $media ) );
15
  $potentialLinkNode = $parent;
16
  $maxDepth = 5;
17
  do {
47
  if ( !empty( $rel ) )
48
  $potentialLinkNode->attr( 'rel', $rel );
49
  if ( !empty( $aria ) )
50
+ $potentialLinkNode->attr['aria-label'] = $aria;
51
  }
52
  return true;
53
  }
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: TigrouMeow
3
  Tags: custom, links, gallery, gutenberg
4
  Donate link: https://commerce.coinbase.com/checkout/d047546a-77a8-41c8-9ea9-4a950f61832f
5
  Requires at least: 5.0
6
- Tested up to: 5.3
7
  Requires PHP: 7.0
8
- Stable tag: 1.2.6
9
 
10
  Gallery Custom Links allows you to link images from galleries to a specified URL. Tested with WordPress Gallery, Gutenberg, the Meow Gallery and others.
11
 
@@ -42,9 +42,13 @@ Replace all the files. Nothing else to do.
42
 
43
  == Changelog ==
44
 
 
 
 
 
 
45
  = 1.2.6 =
46
  * Add: Because we love the W3C, the title has been added to the link.
47
- * Info: If you like the plugin, your reviews are welcome [here](https://wordpress.org/support/plugin/gallery-custom-links/reviews/?rate=5#new-post) :) Thank you!
48
 
49
  = 1.2.5 =
50
  * Fix: Avoid errors in the admin.
3
  Tags: custom, links, gallery, gutenberg
4
  Donate link: https://commerce.coinbase.com/checkout/d047546a-77a8-41c8-9ea9-4a950f61832f
5
  Requires at least: 5.0
6
+ Tested up to: 5.4
7
  Requires PHP: 7.0
8
+ Stable tag: 1.2.7
9
 
10
  Gallery Custom Links allows you to link images from galleries to a specified URL. Tested with WordPress Gallery, Gutenberg, the Meow Gallery and others.
11
 
42
 
43
  == Changelog ==
44
 
45
+ = 1.2.7 =
46
+ * Add: Remove warnings for PHP 7.4.
47
+ * Update: New versions of DiDom and the Simple HTML DOM Parser.
48
+ * Info: If you like the plugin, your reviews are welcome [here](https://wordpress.org/support/plugin/gallery-custom-links/reviews/?rate=5#new-post) :) Thank you!
49
+
50
  = 1.2.6 =
51
  * Add: Because we love the W3C, the title has been added to the link.
 
52
 
53
  = 1.2.5 =
54
  * Fix: Avoid errors in the admin.
vendor/composer/ClassLoader.php CHANGED
@@ -279,7 +279,7 @@ class ClassLoader
279
  */
280
  public function setApcuPrefix($apcuPrefix)
281
  {
282
- $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
283
  }
284
 
285
  /**
279
  */
280
  public function setApcuPrefix($apcuPrefix)
281
  {
282
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
283
  }
284
 
285
  /**
vendor/composer/installed.json CHANGED
@@ -1,17 +1,17 @@
1
  [
2
  {
3
  "name": "imangazaliev/didom",
4
- "version": "1.14.1",
5
- "version_normalized": "1.14.1.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/Imangazaliev/DiDOM.git",
9
- "reference": "a8389cc26897f6bb8843363b4ea16e7ef16fb3c3"
10
  },
11
  "dist": {
12
  "type": "zip",
13
- "url": "https://api.github.com/repos/Imangazaliev/DiDOM/zipball/a8389cc26897f6bb8843363b4ea16e7ef16fb3c3",
14
- "reference": "a8389cc26897f6bb8843363b4ea16e7ef16fb3c3",
15
  "shasum": ""
16
  },
17
  "require": {
@@ -20,10 +20,9 @@
20
  "php": ">=5.4"
21
  },
22
  "require-dev": {
23
- "friendsofphp/php-cs-fixer": "2.9",
24
  "phpunit/phpunit": "^4.8"
25
  },
26
- "time": "2019-01-17T11:01:36+00:00",
27
  "type": "library",
28
  "installation-source": "dist",
29
  "autoload": {
@@ -52,23 +51,23 @@
52
  },
53
  {
54
  "name": "kub-at/php-simple-html-dom-parser",
55
- "version": "1.8.1",
56
- "version_normalized": "1.8.1.0",
57
  "source": {
58
  "type": "git",
59
  "url": "https://github.com/Kub-AT/php-simple-html-dom-parser.git",
60
- "reference": "6db1e01db320040024cd1f74b0e1483aa2670720"
61
  },
62
  "dist": {
63
  "type": "zip",
64
- "url": "https://api.github.com/repos/Kub-AT/php-simple-html-dom-parser/zipball/6db1e01db320040024cd1f74b0e1483aa2670720",
65
- "reference": "6db1e01db320040024cd1f74b0e1483aa2670720",
66
  "shasum": ""
67
  },
68
  "require": {
69
  "php": ">=5.3.2"
70
  },
71
- "time": "2019-03-05T14:12:22+00:00",
72
  "type": "library",
73
  "installation-source": "dist",
74
  "autoload": {
1
  [
2
  {
3
  "name": "imangazaliev/didom",
4
+ "version": "1.16",
5
+ "version_normalized": "1.16.0.0",
6
  "source": {
7
  "type": "git",
8
  "url": "https://github.com/Imangazaliev/DiDOM.git",
9
+ "reference": "c2a8bd08679d2eec856782277267a48553322c7e"
10
  },
11
  "dist": {
12
  "type": "zip",
13
+ "url": "https://api.github.com/repos/Imangazaliev/DiDOM/zipball/c2a8bd08679d2eec856782277267a48553322c7e",
14
+ "reference": "c2a8bd08679d2eec856782277267a48553322c7e",
15
  "shasum": ""
16
  },
17
  "require": {
20
  "php": ">=5.4"
21
  },
22
  "require-dev": {
 
23
  "phpunit/phpunit": "^4.8"
24
  },
25
+ "time": "2020-05-12T21:04:30+00:00",
26
  "type": "library",
27
  "installation-source": "dist",
28
  "autoload": {
51
  },
52
  {
53
  "name": "kub-at/php-simple-html-dom-parser",
54
+ "version": "1.9.1",
55
+ "version_normalized": "1.9.1.0",
56
  "source": {
57
  "type": "git",
58
  "url": "https://github.com/Kub-AT/php-simple-html-dom-parser.git",
59
+ "reference": "ff22f98bfd9235115c128059076f3eb740d66913"
60
  },
61
  "dist": {
62
  "type": "zip",
63
+ "url": "https://api.github.com/repos/Kub-AT/php-simple-html-dom-parser/zipball/ff22f98bfd9235115c128059076f3eb740d66913",
64
+ "reference": "ff22f98bfd9235115c128059076f3eb740d66913",
65
  "shasum": ""
66
  },
67
  "require": {
68
  "php": ">=5.3.2"
69
  },
70
+ "time": "2019-10-25T12:34:43+00:00",
71
  "type": "library",
72
  "installation-source": "dist",
73
  "autoload": {
vendor/imangazaliev/didom/.gitignore CHANGED
@@ -1,7 +1,6 @@
1
  /vendor
2
  /.idea
3
  composer.phar
4
- composer.lock
5
  .php_cs.cache
6
  .DS_Store
7
  Thumbs.db
1
  /vendor
2
  /.idea
3
  composer.phar
 
4
  .php_cs.cache
5
  .DS_Store
6
  Thumbs.db
vendor/imangazaliev/didom/.travis.yml CHANGED
@@ -1,20 +1,21 @@
1
  language: php
2
 
 
 
3
  php:
4
  - 5.4
5
- - 5.5.9
6
  - 5.5
 
7
  - 5.6
8
  - 7.0
 
 
 
9
 
10
  before_script:
11
  - composer self-update
12
  - composer install
13
 
14
  script:
15
- - phpunit
16
 
17
- matrix:
18
- allow_failures:
19
- - php: 5.4
20
- - php: 7.0
1
  language: php
2
 
3
+ dist: trusty
4
+
5
  php:
6
  - 5.4
 
7
  - 5.5
8
+ - 5.5.9
9
  - 5.6
10
  - 7.0
11
+ - 7.1
12
+ - 7.2
13
+ - 7.3
14
 
15
  before_script:
16
  - composer self-update
17
  - composer install
18
 
19
  script:
20
+ - ./vendor/bin/phpunit
21
 
 
 
 
 
vendor/imangazaliev/didom/README-RU.md CHANGED
@@ -1,6 +1,6 @@
1
  # DiDOM
2
 
3
- [![Build Status](https://codeship.com/projects/cf938980-36f0-0134-119e-36dc468776c7/status?branch=master)](https://codeship.com/projects/165662)
4
  [![Total Downloads](https://poser.pugx.org/imangazaliev/didom/downloads)](https://packagist.org/packages/imangazaliev/didom)
5
  [![Latest Stable Version](https://poser.pugx.org/imangazaliev/didom/v/stable)](https://packagist.org/packages/imangazaliev/didom)
6
  [![License](https://poser.pugx.org/imangazaliev/didom/license)](https://packagist.org/packages/imangazaliev/didom)
1
  # DiDOM
2
 
3
+ [![Build Status](https://travis-ci.org/Imangazaliev/DiDOM.svg?branch=master)](https://travis-ci.org/Imangazaliev/DiDOM)
4
  [![Total Downloads](https://poser.pugx.org/imangazaliev/didom/downloads)](https://packagist.org/packages/imangazaliev/didom)
5
  [![Latest Stable Version](https://poser.pugx.org/imangazaliev/didom/v/stable)](https://packagist.org/packages/imangazaliev/didom)
6
  [![License](https://poser.pugx.org/imangazaliev/didom/license)](https://packagist.org/packages/imangazaliev/didom)
vendor/imangazaliev/didom/README.md CHANGED
@@ -1,6 +1,6 @@
1
  # DiDOM
2
 
3
- [![Build Status](https://codeship.com/projects/cf938980-36f0-0134-119e-36dc468776c7/status?branch=master)](https://codeship.com/projects/165662)
4
  [![Total Downloads](https://poser.pugx.org/imangazaliev/didom/downloads)](https://packagist.org/packages/imangazaliev/didom)
5
  [![Latest Stable Version](https://poser.pugx.org/imangazaliev/didom/v/stable)](https://packagist.org/packages/imangazaliev/didom)
6
  [![License](https://poser.pugx.org/imangazaliev/didom/license)](https://packagist.org/packages/imangazaliev/didom)
1
  # DiDOM
2
 
3
+ [![Build Status](https://travis-ci.org/Imangazaliev/DiDOM.svg?branch=master)](https://travis-ci.org/Imangazaliev/DiDOM)
4
  [![Total Downloads](https://poser.pugx.org/imangazaliev/didom/downloads)](https://packagist.org/packages/imangazaliev/didom)
5
  [![Latest Stable Version](https://poser.pugx.org/imangazaliev/didom/v/stable)](https://packagist.org/packages/imangazaliev/didom)
6
  [![License](https://poser.pugx.org/imangazaliev/didom/license)](https://packagist.org/packages/imangazaliev/didom)
vendor/imangazaliev/didom/composer.json CHANGED
@@ -17,8 +17,7 @@
17
  "ext-iconv": "*"
18
  },
19
  "require-dev": {
20
- "phpunit/phpunit": "^4.8",
21
- "friendsofphp/php-cs-fixer": "2.9"
22
  },
23
  "autoload": {
24
  "psr-4": {
@@ -27,7 +26,12 @@
27
  },
28
  "autoload-dev": {
29
  "psr-4": {
30
- "Tests\\": "tests/"
 
 
 
 
 
31
  }
32
  }
33
  }
17
  "ext-iconv": "*"
18
  },
19
  "require-dev": {
20
+ "phpunit/phpunit": "^4.8"
 
21
  },
22
  "autoload": {
23
  "psr-4": {
26
  },
27
  "autoload-dev": {
28
  "psr-4": {
29
+ "DiDom\\Tests\\": "tests/"
30
+ }
31
+ },
32
+ "config": {
33
+ "platform": {
34
+ "php": "5.4"
35
  }
36
  }
37
  }
vendor/imangazaliev/didom/composer.lock ADDED
@@ -0,0 +1,1049 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_readme": [
3
+ "This file locks the dependencies of your project to a known state",
4
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5
+ "This file is @generated automatically"
6
+ ],
7
+ "hash": "7d413af5e53b1204b6c430dbd32ca728",
8
+ "content-hash": "fa5a5b325a0458a9fe05c67fb6b0e719",
9
+ "packages": [],
10
+ "packages-dev": [
11
+ {
12
+ "name": "doctrine/instantiator",
13
+ "version": "1.0.5",
14
+ "source": {
15
+ "type": "git",
16
+ "url": "https://github.com/doctrine/instantiator.git",
17
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
18
+ },
19
+ "dist": {
20
+ "type": "zip",
21
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
22
+ "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
23
+ "shasum": ""
24
+ },
25
+ "require": {
26
+ "php": ">=5.3,<8.0-DEV"
27
+ },
28
+ "require-dev": {
29
+ "athletic/athletic": "~0.1.8",
30
+ "ext-pdo": "*",
31
+ "ext-phar": "*",
32
+ "phpunit/phpunit": "~4.0",
33
+ "squizlabs/php_codesniffer": "~2.0"
34
+ },
35
+ "type": "library",
36
+ "extra": {
37
+ "branch-alias": {
38
+ "dev-master": "1.0.x-dev"
39
+ }
40
+ },
41
+ "autoload": {
42
+ "psr-4": {
43
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
44
+ }
45
+ },
46
+ "notification-url": "https://packagist.org/downloads/",
47
+ "license": [
48
+ "MIT"
49
+ ],
50
+ "authors": [
51
+ {
52
+ "name": "Marco Pivetta",
53
+ "email": "ocramius@gmail.com",
54
+ "homepage": "http://ocramius.github.com/"
55
+ }
56
+ ],
57
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
58
+ "homepage": "https://github.com/doctrine/instantiator",
59
+ "keywords": [
60
+ "constructor",
61
+ "instantiate"
62
+ ],
63
+ "time": "2015-06-14 21:17:01"
64
+ },
65
+ {
66
+ "name": "phpdocumentor/reflection-docblock",
67
+ "version": "2.0.5",
68
+ "source": {
69
+ "type": "git",
70
+ "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
71
+ "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b"
72
+ },
73
+ "dist": {
74
+ "type": "zip",
75
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e6a969a640b00d8daa3c66518b0405fb41ae0c4b",
76
+ "reference": "e6a969a640b00d8daa3c66518b0405fb41ae0c4b",
77
+ "shasum": ""
78
+ },
79
+ "require": {
80
+ "php": ">=5.3.3"
81
+ },
82
+ "require-dev": {
83
+ "phpunit/phpunit": "~4.0"
84
+ },
85
+ "suggest": {
86
+ "dflydev/markdown": "~1.0",
87
+ "erusev/parsedown": "~1.0"
88
+ },
89
+ "type": "library",
90
+ "extra": {
91
+ "branch-alias": {
92
+ "dev-master": "2.0.x-dev"
93
+ }
94
+ },
95
+ "autoload": {
96
+ "psr-0": {
97
+ "phpDocumentor": [
98
+ "src/"
99
+ ]
100
+ }
101
+ },
102
+ "notification-url": "https://packagist.org/downloads/",
103
+ "license": [
104
+ "MIT"
105
+ ],
106
+ "authors": [
107
+ {
108
+ "name": "Mike van Riel",
109
+ "email": "mike.vanriel@naenius.com"
110
+ }
111
+ ],
112
+ "time": "2016-01-25 08:17:30"
113
+ },
114
+ {
115
+ "name": "phpspec/prophecy",
116
+ "version": "1.8.1",
117
+ "source": {
118
+ "type": "git",
119
+ "url": "https://github.com/phpspec/prophecy.git",
120
+ "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
121
+ },
122
+ "dist": {
123
+ "type": "zip",
124
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
125
+ "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
126
+ "shasum": ""
127
+ },
128
+ "require": {
129
+ "doctrine/instantiator": "^1.0.2",
130
+ "php": "^5.3|^7.0",
131
+ "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
132
+ "sebastian/comparator": "^1.1|^2.0|^3.0",
133
+ "sebastian/recursion-context": "^1.0|^2.0|^3.0"
134
+ },
135
+ "require-dev": {
136
+ "phpspec/phpspec": "^2.5|^3.2",
137
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
138
+ },
139
+ "type": "library",
140
+ "extra": {
141
+ "branch-alias": {
142
+ "dev-master": "1.8.x-dev"
143
+ }
144
+ },
145
+ "autoload": {
146
+ "psr-4": {
147
+ "Prophecy\\": "src/Prophecy"
148
+ }
149
+ },
150
+ "notification-url": "https://packagist.org/downloads/",
151
+ "license": [
152
+ "MIT"
153
+ ],
154
+ "authors": [
155
+ {
156
+ "name": "Konstantin Kudryashov",
157
+ "email": "ever.zet@gmail.com",
158
+ "homepage": "http://everzet.com"
159
+ },
160
+ {
161
+ "name": "Marcello Duarte",
162
+ "email": "marcello.duarte@gmail.com"
163
+ }
164
+ ],
165
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
166
+ "homepage": "https://github.com/phpspec/prophecy",
167
+ "keywords": [
168
+ "Double",
169
+ "Dummy",
170
+ "fake",
171
+ "mock",
172
+ "spy",
173
+ "stub"
174
+ ],
175
+ "time": "2019-06-13 12:50:23"
176
+ },
177
+ {
178
+ "name": "phpunit/php-code-coverage",
179
+ "version": "2.2.4",
180
+ "source": {
181
+ "type": "git",
182
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
183
+ "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
184
+ },
185
+ "dist": {
186
+ "type": "zip",
187
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
188
+ "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
189
+ "shasum": ""
190
+ },
191
+ "require": {
192
+ "php": ">=5.3.3",
193
+ "phpunit/php-file-iterator": "~1.3",
194
+ "phpunit/php-text-template": "~1.2",
195
+ "phpunit/php-token-stream": "~1.3",
196
+ "sebastian/environment": "^1.3.2",
197
+ "sebastian/version": "~1.0"
198
+ },
199
+ "require-dev": {
200
+ "ext-xdebug": ">=2.1.4",
201
+ "phpunit/phpunit": "~4"
202
+ },
203
+ "suggest": {
204
+ "ext-dom": "*",
205
+ "ext-xdebug": ">=2.2.1",
206
+ "ext-xmlwriter": "*"
207
+ },
208
+ "type": "library",
209
+ "extra": {
210
+ "branch-alias": {
211
+ "dev-master": "2.2.x-dev"
212
+ }
213
+ },
214
+ "autoload": {
215
+ "classmap": [
216
+ "src/"
217
+ ]
218
+ },
219
+ "notification-url": "https://packagist.org/downloads/",
220
+ "license": [
221
+ "BSD-3-Clause"
222
+ ],
223
+ "authors": [
224
+ {
225
+ "name": "Sebastian Bergmann",
226
+ "email": "sb@sebastian-bergmann.de",
227
+ "role": "lead"
228
+ }
229
+ ],
230
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
231
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
232
+ "keywords": [
233
+ "coverage",
234
+ "testing",
235
+ "xunit"
236
+ ],
237
+ "time": "2015-10-06 15:47:00"
238
+ },
239
+ {
240
+ "name": "phpunit/php-file-iterator",
241
+ "version": "1.4.5",
242
+ "source": {
243
+ "type": "git",
244
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
245
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
246
+ },
247
+ "dist": {
248
+ "type": "zip",
249
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
250
+ "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
251
+ "shasum": ""
252
+ },
253
+ "require": {
254
+ "php": ">=5.3.3"
255
+ },
256
+ "type": "library",
257
+ "extra": {
258
+ "branch-alias": {
259
+ "dev-master": "1.4.x-dev"
260
+ }
261
+ },
262
+ "autoload": {
263
+ "classmap": [
264
+ "src/"
265
+ ]
266
+ },
267
+ "notification-url": "https://packagist.org/downloads/",
268
+ "license": [
269
+ "BSD-3-Clause"
270
+ ],
271
+ "authors": [
272
+ {
273
+ "name": "Sebastian Bergmann",
274
+ "email": "sb@sebastian-bergmann.de",
275
+ "role": "lead"
276
+ }
277
+ ],
278
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
279
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
280
+ "keywords": [
281
+ "filesystem",
282
+ "iterator"
283
+ ],
284
+ "time": "2017-11-27 13:52:08"
285
+ },
286
+ {
287
+ "name": "phpunit/php-text-template",
288
+ "version": "1.2.1",
289
+ "source": {
290
+ "type": "git",
291
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
292
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
293
+ },
294
+ "dist": {
295
+ "type": "zip",
296
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
297
+ "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
298
+ "shasum": ""
299
+ },
300
+ "require": {
301
+ "php": ">=5.3.3"
302
+ },
303
+ "type": "library",
304
+ "autoload": {
305
+ "classmap": [
306
+ "src/"
307
+ ]
308
+ },
309
+ "notification-url": "https://packagist.org/downloads/",
310
+ "license": [
311
+ "BSD-3-Clause"
312
+ ],
313
+ "authors": [
314
+ {
315
+ "name": "Sebastian Bergmann",
316
+ "email": "sebastian@phpunit.de",
317
+ "role": "lead"
318
+ }
319
+ ],
320
+ "description": "Simple template engine.",
321
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
322
+ "keywords": [
323
+ "template"
324
+ ],
325
+ "time": "2015-06-21 13:50:34"
326
+ },
327
+ {
328
+ "name": "phpunit/php-timer",
329
+ "version": "1.0.9",
330
+ "source": {
331
+ "type": "git",
332
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
333
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
334
+ },
335
+ "dist": {
336
+ "type": "zip",
337
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
338
+ "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
339
+ "shasum": ""
340
+ },
341
+ "require": {
342
+ "php": "^5.3.3 || ^7.0"
343
+ },
344
+ "require-dev": {
345
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
346
+ },
347
+ "type": "library",
348
+ "extra": {
349
+ "branch-alias": {
350
+ "dev-master": "1.0-dev"
351
+ }
352
+ },
353
+ "autoload": {
354
+ "classmap": [
355
+ "src/"
356
+ ]
357
+ },
358
+ "notification-url": "https://packagist.org/downloads/",
359
+ "license": [
360
+ "BSD-3-Clause"
361
+ ],
362
+ "authors": [
363
+ {
364
+ "name": "Sebastian Bergmann",
365
+ "email": "sb@sebastian-bergmann.de",
366
+ "role": "lead"
367
+ }
368
+ ],
369
+ "description": "Utility class for timing",
370
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
371
+ "keywords": [
372
+ "timer"
373
+ ],
374
+ "time": "2017-02-26 11:10:40"
375
+ },
376
+ {
377
+ "name": "phpunit/php-token-stream",
378
+ "version": "1.4.12",
379
+ "source": {
380
+ "type": "git",
381
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
382
+ "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16"
383
+ },
384
+ "dist": {
385
+ "type": "zip",
386
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16",
387
+ "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16",
388
+ "shasum": ""
389
+ },
390
+ "require": {
391
+ "ext-tokenizer": "*",
392
+ "php": ">=5.3.3"
393
+ },
394
+ "require-dev": {
395
+ "phpunit/phpunit": "~4.2"
396
+ },
397
+ "type": "library",
398
+ "extra": {
399
+ "branch-alias": {
400
+ "dev-master": "1.4-dev"
401
+ }
402
+ },
403
+ "autoload": {
404
+ "classmap": [
405
+ "src/"
406
+ ]
407
+ },
408
+ "notification-url": "https://packagist.org/downloads/",
409
+ "license": [
410
+ "BSD-3-Clause"
411
+ ],
412
+ "authors": [
413
+ {
414
+ "name": "Sebastian Bergmann",
415
+ "email": "sebastian@phpunit.de"
416
+ }
417
+ ],
418
+ "description": "Wrapper around PHP's tokenizer extension.",
419
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
420
+ "keywords": [
421
+ "tokenizer"
422
+ ],
423
+ "time": "2017-12-04 08:55:13"
424
+ },
425
+ {
426
+ "name": "phpunit/phpunit",
427
+ "version": "4.8.36",
428
+ "source": {
429
+ "type": "git",
430
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
431
+ "reference": "46023de9a91eec7dfb06cc56cb4e260017298517"
432
+ },
433
+ "dist": {
434
+ "type": "zip",
435
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517",
436
+ "reference": "46023de9a91eec7dfb06cc56cb4e260017298517",
437
+ "shasum": ""
438
+ },
439
+ "require": {
440
+ "ext-dom": "*",
441
+ "ext-json": "*",
442
+ "ext-pcre": "*",
443
+ "ext-reflection": "*",
444
+ "ext-spl": "*",
445
+ "php": ">=5.3.3",
446
+ "phpspec/prophecy": "^1.3.1",
447
+ "phpunit/php-code-coverage": "~2.1",
448
+ "phpunit/php-file-iterator": "~1.4",
449
+ "phpunit/php-text-template": "~1.2",
450
+ "phpunit/php-timer": "^1.0.6",
451
+ "phpunit/phpunit-mock-objects": "~2.3",
452
+ "sebastian/comparator": "~1.2.2",
453
+ "sebastian/diff": "~1.2",
454
+ "sebastian/environment": "~1.3",
455
+ "sebastian/exporter": "~1.2",
456
+ "sebastian/global-state": "~1.0",
457
+ "sebastian/version": "~1.0",
458
+ "symfony/yaml": "~2.1|~3.0"
459
+ },
460
+ "suggest": {
461
+ "phpunit/php-invoker": "~1.1"
462
+ },
463
+ "bin": [
464
+ "phpunit"
465
+ ],
466
+ "type": "library",
467
+ "extra": {
468
+ "branch-alias": {
469
+ "dev-master": "4.8.x-dev"
470
+ }
471
+ },
472
+ "autoload": {
473
+ "classmap": [
474
+ "src/"
475
+ ]
476
+ },
477
+ "notification-url": "https://packagist.org/downloads/",
478
+ "license": [
479
+ "BSD-3-Clause"
480
+ ],
481
+ "authors": [
482
+ {
483
+ "name": "Sebastian Bergmann",
484
+ "email": "sebastian@phpunit.de",
485
+ "role": "lead"
486
+ }
487
+ ],
488
+ "description": "The PHP Unit Testing framework.",
489
+ "homepage": "https://phpunit.de/",
490
+ "keywords": [
491
+ "phpunit",
492
+ "testing",
493
+ "xunit"
494
+ ],
495
+ "time": "2017-06-21 08:07:12"
496
+ },
497
+ {
498
+ "name": "phpunit/phpunit-mock-objects",
499
+ "version": "2.3.8",
500
+ "source": {
501
+ "type": "git",
502
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
503
+ "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
504
+ },
505
+ "dist": {
506
+ "type": "zip",
507
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
508
+ "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
509
+ "shasum": ""
510
+ },
511
+ "require": {
512
+ "doctrine/instantiator": "^1.0.2",
513
+ "php": ">=5.3.3",
514
+ "phpunit/php-text-template": "~1.2",
515
+ "sebastian/exporter": "~1.2"
516
+ },
517
+ "require-dev": {
518
+ "phpunit/phpunit": "~4.4"
519
+ },
520
+ "suggest": {
521
+ "ext-soap": "*"
522
+ },
523
+ "type": "library",
524
+ "extra": {
525
+ "branch-alias": {
526
+ "dev-master": "2.3.x-dev"
527
+ }
528
+ },
529
+ "autoload": {
530
+ "classmap": [
531
+ "src/"
532
+ ]
533
+ },
534
+ "notification-url": "https://packagist.org/downloads/",
535
+ "license": [
536
+ "BSD-3-Clause"
537
+ ],
538
+ "authors": [
539
+ {
540
+ "name": "Sebastian Bergmann",
541
+ "email": "sb@sebastian-bergmann.de",
542
+ "role": "lead"
543
+ }
544
+ ],
545
+ "description": "Mock Object library for PHPUnit",
546
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
547
+ "keywords": [
548
+ "mock",
549
+ "xunit"
550
+ ],
551
+ "abandoned": true,
552
+ "time": "2015-10-02 06:51:40"
553
+ },
554
+ {
555
+ "name": "sebastian/comparator",
556
+ "version": "1.2.4",
557
+ "source": {
558
+ "type": "git",
559
+ "url": "https://github.com/sebastianbergmann/comparator.git",
560
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
561
+ },
562
+ "dist": {
563
+ "type": "zip",
564
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
565
+ "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
566
+ "shasum": ""
567
+ },
568
+ "require": {
569
+ "php": ">=5.3.3",
570
+ "sebastian/diff": "~1.2",
571
+ "sebastian/exporter": "~1.2 || ~2.0"
572
+ },
573
+ "require-dev": {
574
+ "phpunit/phpunit": "~4.4"
575
+ },
576
+ "type": "library",
577
+ "extra": {
578
+ "branch-alias": {
579
+ "dev-master": "1.2.x-dev"
580
+ }
581
+ },
582
+ "autoload": {
583
+ "classmap": [
584
+ "src/"
585
+ ]
586
+ },
587
+ "notification-url": "https://packagist.org/downloads/",
588
+ "license": [
589
+ "BSD-3-Clause"
590
+ ],
591
+ "authors": [
592
+ {
593
+ "name": "Jeff Welch",
594
+ "email": "whatthejeff@gmail.com"
595
+ },
596
+ {
597
+ "name": "Volker Dusch",
598
+ "email": "github@wallbash.com"
599
+ },
600
+ {
601
+ "name": "Bernhard Schussek",
602
+ "email": "bschussek@2bepublished.at"
603
+ },
604
+ {
605
+ "name": "Sebastian Bergmann",
606
+ "email": "sebastian@phpunit.de"
607
+ }
608
+ ],
609
+ "description": "Provides the functionality to compare PHP values for equality",
610
+ "homepage": "http://www.github.com/sebastianbergmann/comparator",
611
+ "keywords": [
612
+ "comparator",
613
+ "compare",
614
+ "equality"
615
+ ],
616
+ "time": "2017-01-29 09:50:25"
617
+ },
618
+ {
619
+ "name": "sebastian/diff",
620
+ "version": "1.4.3",
621
+ "source": {
622
+ "type": "git",
623
+ "url": "https://github.com/sebastianbergmann/diff.git",
624
+ "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
625
+ },
626
+ "dist": {
627
+ "type": "zip",
628
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
629
+ "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
630
+ "shasum": ""
631
+ },
632
+ "require": {
633
+ "php": "^5.3.3 || ^7.0"
634
+ },
635
+ "require-dev": {
636
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
637
+ },
638
+ "type": "library",
639
+ "extra": {
640
+ "branch-alias": {
641
+ "dev-master": "1.4-dev"
642
+ }
643
+ },
644
+ "autoload": {
645
+ "classmap": [
646
+ "src/"
647
+ ]
648
+ },
649
+ "notification-url": "https://packagist.org/downloads/",
650
+ "license": [
651
+ "BSD-3-Clause"
652
+ ],
653
+ "authors": [
654
+ {
655
+ "name": "Kore Nordmann",
656
+ "email": "mail@kore-nordmann.de"
657
+ },
658
+ {
659
+ "name": "Sebastian Bergmann",
660
+ "email": "sebastian@phpunit.de"
661
+ }
662
+ ],
663
+ "description": "Diff implementation",
664
+ "homepage": "https://github.com/sebastianbergmann/diff",
665
+ "keywords": [
666
+ "diff"
667
+ ],
668
+ "time": "2017-05-22 07:24:03"
669
+ },
670
+ {
671
+ "name": "sebastian/environment",
672
+ "version": "1.3.8",
673
+ "source": {
674
+ "type": "git",
675
+ "url": "https://github.com/sebastianbergmann/environment.git",
676
+ "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
677
+ },
678
+ "dist": {
679
+ "type": "zip",
680
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
681
+ "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
682
+ "shasum": ""
683
+ },
684
+ "require": {
685
+ "php": "^5.3.3 || ^7.0"
686
+ },
687
+ "require-dev": {
688
+ "phpunit/phpunit": "^4.8 || ^5.0"
689
+ },
690
+ "type": "library",
691
+ "extra": {
692
+ "branch-alias": {
693
+ "dev-master": "1.3.x-dev"
694
+ }
695
+ },
696
+ "autoload": {
697
+ "classmap": [
698
+ "src/"
699
+ ]
700
+ },
701
+ "notification-url": "https://packagist.org/downloads/",
702
+ "license": [
703
+ "BSD-3-Clause"
704
+ ],
705
+ "authors": [
706
+ {
707
+ "name": "Sebastian Bergmann",
708
+ "email": "sebastian@phpunit.de"
709
+ }
710
+ ],
711
+ "description": "Provides functionality to handle HHVM/PHP environments",
712
+ "homepage": "http://www.github.com/sebastianbergmann/environment",
713
+ "keywords": [
714
+ "Xdebug",
715
+ "environment",
716
+ "hhvm"
717
+ ],
718
+ "time": "2016-08-18 05:49:44"
719
+ },
720
+ {
721
+ "name": "sebastian/exporter",
722
+ "version": "1.2.2",
723
+ "source": {
724
+ "type": "git",
725
+ "url": "https://github.com/sebastianbergmann/exporter.git",
726
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
727
+ },
728
+ "dist": {
729
+ "type": "zip",
730
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
731
+ "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
732
+ "shasum": ""
733
+ },
734
+ "require": {
735
+ "php": ">=5.3.3",
736
+ "sebastian/recursion-context": "~1.0"
737
+ },
738
+ "require-dev": {
739
+ "ext-mbstring": "*",
740
+ "phpunit/phpunit": "~4.4"
741
+ },
742
+ "type": "library",
743
+ "extra": {
744
+ "branch-alias": {
745
+ "dev-master": "1.3.x-dev"
746
+ }
747
+ },
748
+ "autoload": {
749
+ "classmap": [
750
+ "src/"
751
+ ]
752
+ },
753
+ "notification-url": "https://packagist.org/downloads/",
754
+ "license": [
755
+ "BSD-3-Clause"
756
+ ],
757
+ "authors": [
758
+ {
759
+ "name": "Jeff Welch",
760
+ "email": "whatthejeff@gmail.com"
761
+ },
762
+ {
763
+ "name": "Volker Dusch",
764
+ "email": "github@wallbash.com"
765
+ },
766
+ {
767
+ "name": "Bernhard Schussek",
768
+ "email": "bschussek@2bepublished.at"
769
+ },
770
+ {
771
+ "name": "Sebastian Bergmann",
772
+ "email": "sebastian@phpunit.de"
773
+ },
774
+ {
775
+ "name": "Adam Harvey",
776
+ "email": "aharvey@php.net"
777
+ }
778
+ ],
779
+ "description": "Provides the functionality to export PHP variables for visualization",
780
+ "homepage": "http://www.github.com/sebastianbergmann/exporter",
781
+ "keywords": [
782
+ "export",
783
+ "exporter"
784
+ ],
785
+ "time": "2016-06-17 09:04:28"
786
+ },
787
+ {
788
+ "name": "sebastian/global-state",
789
+ "version": "1.1.1",
790
+ "source": {
791
+ "type": "git",
792
+ "url": "https://github.com/sebastianbergmann/global-state.git",
793
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
794
+ },
795
+ "dist": {
796
+ "type": "zip",
797
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
798
+ "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
799
+ "shasum": ""
800
+ },
801
+ "require": {
802
+ "php": ">=5.3.3"
803
+ },
804
+ "require-dev": {
805
+ "phpunit/phpunit": "~4.2"
806
+ },
807
+ "suggest": {
808
+ "ext-uopz": "*"
809
+ },
810
+ "type": "library",
811
+ "extra": {
812
+ "branch-alias": {
813
+ "dev-master": "1.0-dev"
814
+ }
815
+ },
816
+ "autoload": {
817
+ "classmap": [
818
+ "src/"
819
+ ]
820
+ },
821
+ "notification-url": "https://packagist.org/downloads/",
822
+ "license": [
823
+ "BSD-3-Clause"
824
+ ],
825
+ "authors": [
826
+ {
827
+ "name": "Sebastian Bergmann",
828
+ "email": "sebastian@phpunit.de"
829
+ }
830
+ ],
831
+ "description": "Snapshotting of global state",
832
+ "homepage": "http://www.github.com/sebastianbergmann/global-state",
833
+ "keywords": [
834
+ "global state"
835
+ ],
836
+ "time": "2015-10-12 03:26:01"
837
+ },
838
+ {
839
+ "name": "sebastian/recursion-context",
840
+ "version": "1.0.5",
841
+ "source": {
842
+ "type": "git",
843
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
844
+ "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
845
+ },
846
+ "dist": {
847
+ "type": "zip",
848
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
849
+ "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
850
+ "shasum": ""
851
+ },
852
+ "require": {
853
+ "php": ">=5.3.3"
854
+ },
855
+ "require-dev": {
856
+ "phpunit/phpunit": "~4.4"
857
+ },
858
+ "type": "library",
859
+ "extra": {
860
+ "branch-alias": {
861
+ "dev-master": "1.0.x-dev"
862
+ }
863
+ },
864
+ "autoload": {
865
+ "classmap": [
866
+ "src/"
867
+ ]
868
+ },
869
+ "notification-url": "https://packagist.org/downloads/",
870
+ "license": [
871
+ "BSD-3-Clause"
872
+ ],
873
+ "authors": [
874
+ {
875
+ "name": "Jeff Welch",
876
+ "email": "whatthejeff@gmail.com"
877
+ },
878
+ {
879
+ "name": "Sebastian Bergmann",
880
+ "email": "sebastian@phpunit.de"
881
+ },
882
+ {
883
+ "name": "Adam Harvey",
884
+ "email": "aharvey@php.net"
885
+ }
886
+ ],
887
+ "description": "Provides functionality to recursively process PHP variables",
888
+ "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
889
+ "time": "2016-10-03 07:41:43"
890
+ },
891
+ {
892
+ "name": "sebastian/version",
893
+ "version": "1.0.6",
894
+ "source": {
895
+ "type": "git",
896
+ "url": "https://github.com/sebastianbergmann/version.git",
897
+ "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
898
+ },
899
+ "dist": {
900
+ "type": "zip",
901
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
902
+ "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
903
+ "shasum": ""
904
+ },
905
+ "type": "library",
906
+ "autoload": {
907
+ "classmap": [
908
+ "src/"
909
+ ]
910
+ },
911
+ "notification-url": "https://packagist.org/downloads/",
912
+ "license": [
913
+ "BSD-3-Clause"
914
+ ],
915
+ "authors": [
916
+ {
917
+ "name": "Sebastian Bergmann",
918
+ "email": "sebastian@phpunit.de",
919
+ "role": "lead"
920
+ }
921
+ ],
922
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
923
+ "homepage": "https://github.com/sebastianbergmann/version",
924
+ "time": "2015-06-21 13:59:46"
925
+ },
926
+ {
927
+ "name": "symfony/polyfill-ctype",
928
+ "version": "v1.12.0",
929
+ "source": {
930
+ "type": "git",
931
+ "url": "https://github.com/symfony/polyfill-ctype.git",
932
+ "reference": "550ebaac289296ce228a706d0867afc34687e3f4"
933
+ },
934
+ "dist": {
935
+ "type": "zip",
936
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
937
+ "reference": "550ebaac289296ce228a706d0867afc34687e3f4",
938
+ "shasum": ""
939
+ },
940
+ "require": {
941
+ "php": ">=5.3.3"
942
+ },
943
+ "suggest": {
944
+ "ext-ctype": "For best performance"
945
+ },
946
+ "type": "library",
947
+ "extra": {
948
+ "branch-alias": {
949
+ "dev-master": "1.12-dev"
950
+ }
951
+ },
952
+ "autoload": {
953
+ "psr-4": {
954
+ "Symfony\\Polyfill\\Ctype\\": ""
955
+ },
956
+ "files": [
957
+ "bootstrap.php"
958
+ ]
959
+ },
960
+ "notification-url": "https://packagist.org/downloads/",
961
+ "license": [
962
+ "MIT"
963
+ ],
964
+ "authors": [
965
+ {
966
+ "name": "Gert de Pagter",
967
+ "email": "BackEndTea@gmail.com"
968
+ },
969
+ {
970
+ "name": "Symfony Community",
971
+ "homepage": "https://symfony.com/contributors"
972
+ }
973
+ ],
974
+ "description": "Symfony polyfill for ctype functions",
975
+ "homepage": "https://symfony.com",
976
+ "keywords": [
977
+ "compatibility",
978
+ "ctype",
979
+ "polyfill",
980
+ "portable"
981
+ ],
982
+ "time": "2019-08-06 08:03:45"
983
+ },
984
+ {
985
+ "name": "symfony/yaml",
986
+ "version": "v2.8.50",
987
+ "source": {
988
+ "type": "git",
989
+ "url": "https://github.com/symfony/yaml.git",
990
+ "reference": "02c1859112aa779d9ab394ae4f3381911d84052b"
991
+ },
992
+ "dist": {
993
+ "type": "zip",
994
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
995
+ "reference": "02c1859112aa779d9ab394ae4f3381911d84052b",
996
+ "shasum": ""
997
+ },
998
+ "require": {
999
+ "php": ">=5.3.9",
1000
+ "symfony/polyfill-ctype": "~1.8"
1001
+ },
1002
+ "type": "library",
1003
+ "extra": {
1004
+ "branch-alias": {
1005
+ "dev-master": "2.8-dev"
1006
+ }
1007
+ },
1008
+ "autoload": {
1009
+ "psr-4": {
1010
+ "Symfony\\Component\\Yaml\\": ""
1011
+ },
1012
+ "exclude-from-classmap": [
1013
+ "/Tests/"
1014
+ ]
1015
+ },
1016
+ "notification-url": "https://packagist.org/downloads/",
1017
+ "license": [
1018
+ "MIT"
1019
+ ],
1020
+ "authors": [
1021
+ {
1022
+ "name": "Fabien Potencier",
1023
+ "email": "fabien@symfony.com"
1024
+ },
1025
+ {
1026
+ "name": "Symfony Community",
1027
+ "homepage": "https://symfony.com/contributors"
1028
+ }
1029
+ ],
1030
+ "description": "Symfony Yaml Component",
1031
+ "homepage": "https://symfony.com",
1032
+ "time": "2018-11-11 11:18:13"
1033
+ }
1034
+ ],
1035
+ "aliases": [],
1036
+ "minimum-stability": "stable",
1037
+ "stability-flags": [],
1038
+ "prefer-stable": false,
1039
+ "prefer-lowest": false,
1040
+ "platform": {
1041
+ "php": ">=5.4",
1042
+ "ext-dom": "*",
1043
+ "ext-iconv": "*"
1044
+ },
1045
+ "platform-dev": [],
1046
+ "platform-overrides": {
1047
+ "php": "5.4"
1048
+ }
1049
+ }
vendor/imangazaliev/didom/phpunit.xml CHANGED
@@ -1,17 +1,18 @@
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <phpunit backupGlobals="false"
3
- backupStaticAttributes="false"
4
- bootstrap="tests/bootstrap.php"
5
- colors="true"
6
- convertErrorsToExceptions="true"
7
- convertNoticesToExceptions="true"
8
- convertWarningsToExceptions="true"
9
- processIsolation="false"
10
- stopOnFailure="false"
11
- syntaxCheck="false">
 
12
  <testsuites>
13
  <testsuite name="DiDom Test Suite">
14
  <directory>./tests/</directory>
15
  </testsuite>
16
  </testsuites>
17
- </phpunit>
1
  <?xml version="1.0" encoding="UTF-8"?>
2
+ <phpunit
3
+ backupGlobals="false"
4
+ backupStaticAttributes="false"
5
+ bootstrap="tests/bootstrap.php"
6
+ colors="true"
7
+ convertErrorsToExceptions="true"
8
+ convertNoticesToExceptions="true"
9
+ convertWarningsToExceptions="true"
10
+ processIsolation="false"
11
+ stopOnFailure="false"
12
+ >
13
  <testsuites>
14
  <testsuite name="DiDom Test Suite">
15
  <directory>./tests/</directory>
16
  </testsuite>
17
  </testsuites>
18
+ </phpunit>
vendor/imangazaliev/didom/src/DiDom/ClassAttribute.php CHANGED
@@ -9,7 +9,7 @@ class ClassAttribute
9
  /**
10
  * The DOM element instance.
11
  *
12
- * @var \DiDom\Element
13
  */
14
  protected $element;
15
 
@@ -24,15 +24,13 @@ class ClassAttribute
24
  protected $classes = [];
25
 
26
  /**
27
- * Constructor.
28
  *
29
- * @param \DiDom\Element $element
30
- *
31
- * @throws \InvalidArgumentException if passed argument is not an element node
32
  */
33
  public function __construct(Element $element)
34
  {
35
- if (!$element->isElementNode()) {
36
  throw new InvalidArgumentException(sprintf('The element must contain DOMElement node'));
37
  }
38
 
@@ -46,7 +44,7 @@ class ClassAttribute
46
  */
47
  protected function parseClassAttribute()
48
  {
49
- if (!$this->element->hasAttribute('class')) {
50
  // possible if class attribute has been removed
51
  if ($this->classesString !== '') {
52
  $this->classesString = '';
@@ -94,13 +92,13 @@ class ClassAttribute
94
  /**
95
  * @param string $className
96
  *
97
- * @return \DiDom\ClassAttribute
98
  *
99
- * @throws \InvalidArgumentException if class name is not a string
100
  */
101
  public function add($className)
102
  {
103
- if (!is_string($className)) {
104
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
105
  }
106
 
@@ -120,16 +118,16 @@ class ClassAttribute
120
  /**
121
  * @param array $classNames
122
  *
123
- * @return \DiDom\ClassAttribute
124
  *
125
- * @throws \InvalidArgumentException if class name is not a string
126
  */
127
  public function addMultiple(array $classNames)
128
  {
129
  $this->parseClassAttribute();
130
 
131
  foreach ($classNames as $className) {
132
- if (!is_string($className)) {
133
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
134
  }
135
 
@@ -162,7 +160,7 @@ class ClassAttribute
162
  */
163
  public function contains($className)
164
  {
165
- if (!is_string($className)) {
166
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
167
  }
168
 
@@ -174,13 +172,13 @@ class ClassAttribute
174
  /**
175
  * @param string $className
176
  *
177
- * @return \DiDom\ClassAttribute
178
  *
179
- * @throws \InvalidArgumentException if class name is not a string
180
  */
181
  public function remove($className)
182
  {
183
- if (!is_string($className)) {
184
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
185
  }
186
 
@@ -202,16 +200,16 @@ class ClassAttribute
202
  /**
203
  * @param array $classNames
204
  *
205
- * @return \DiDom\ClassAttribute
206
  *
207
- * @throws \InvalidArgumentException if class name is not a string
208
  */
209
  public function removeMultiple(array $classNames)
210
  {
211
  $this->parseClassAttribute();
212
 
213
  foreach ($classNames as $className) {
214
- if (!is_string($className)) {
215
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
216
  }
217
 
@@ -232,7 +230,7 @@ class ClassAttribute
232
  /**
233
  * @param string[] $exclusions
234
  *
235
- * @return \DiDom\ClassAttribute
236
  */
237
  public function removeAll(array $exclusions = [])
238
  {
@@ -241,11 +239,11 @@ class ClassAttribute
241
  $preservedClasses = [];
242
 
243
  foreach ($exclusions as $className) {
244
- if (!is_string($className)) {
245
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
246
  }
247
 
248
- if (!in_array($className, $this->classes, true)) {
249
  continue;
250
  }
251
 
@@ -260,7 +258,7 @@ class ClassAttribute
260
  }
261
 
262
  /**
263
- * @return \DiDom\Element
264
  */
265
  public function getElement()
266
  {
9
  /**
10
  * The DOM element instance.
11
  *
12
+ * @var Element
13
  */
14
  protected $element;
15
 
24
  protected $classes = [];
25
 
26
  /**
27
+ * @param Element $element
28
  *
29
+ * @throws InvalidArgumentException if parameter 1 is not an element node
 
 
30
  */
31
  public function __construct(Element $element)
32
  {
33
+ if ( ! $element->isElementNode()) {
34
  throw new InvalidArgumentException(sprintf('The element must contain DOMElement node'));
35
  }
36
 
44
  */
45
  protected function parseClassAttribute()
46
  {
47
+ if ( ! $this->element->hasAttribute('class')) {
48
  // possible if class attribute has been removed
49
  if ($this->classesString !== '') {
50
  $this->classesString = '';
92
  /**
93
  * @param string $className
94
  *
95
+ * @return ClassAttribute
96
  *
97
+ * @throws InvalidArgumentException if class name is not a string
98
  */
99
  public function add($className)
100
  {
101
+ if ( ! is_string($className)) {
102
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
103
  }
104
 
118
  /**
119
  * @param array $classNames
120
  *
121
+ * @return ClassAttribute
122
  *
123
+ * @throws InvalidArgumentException if class name is not a string
124
  */
125
  public function addMultiple(array $classNames)
126
  {
127
  $this->parseClassAttribute();
128
 
129
  foreach ($classNames as $className) {
130
+ if ( ! is_string($className)) {
131
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
132
  }
133
 
160
  */
161
  public function contains($className)
162
  {
163
+ if ( ! is_string($className)) {
164
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
165
  }
166
 
172
  /**
173
  * @param string $className
174
  *
175
+ * @return ClassAttribute
176
  *
177
+ * @throws InvalidArgumentException if class name is not a string
178
  */
179
  public function remove($className)
180
  {
181
+ if ( ! is_string($className)) {
182
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($className) ? get_class($className) : gettype($className))));
183
  }
184
 
200
  /**
201
  * @param array $classNames
202
  *
203
+ * @return ClassAttribute
204
  *
205
+ * @throws InvalidArgumentException if class name is not a string
206
  */
207
  public function removeMultiple(array $classNames)
208
  {
209
  $this->parseClassAttribute();
210
 
211
  foreach ($classNames as $className) {
212
+ if ( ! is_string($className)) {
213
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
214
  }
215
 
230
  /**
231
  * @param string[] $exclusions
232
  *
233
+ * @return ClassAttribute
234
  */
235
  public function removeAll(array $exclusions = [])
236
  {
239
  $preservedClasses = [];
240
 
241
  foreach ($exclusions as $className) {
242
+ if ( ! is_string($className)) {
243
  throw new InvalidArgumentException(sprintf('Class name must be a string, %s given', (is_object($className) ? get_class($className) : gettype($className))));
244
  }
245
 
246
+ if ( ! in_array($className, $this->classes, true)) {
247
  continue;
248
  }
249
 
258
  }
259
 
260
  /**
261
+ * @return Element
262
  */
263
  public function getElement()
264
  {
vendor/imangazaliev/didom/src/DiDom/Document.php CHANGED
@@ -2,6 +2,8 @@
2
 
3
  namespace DiDom;
4
 
 
 
5
  use DOMCdataSection;
6
  use DOMComment;
7
  use DOMDocument;
@@ -9,6 +11,7 @@ use DOMElement;
9
  use DOMNode;
10
  use DOMText;
11
  use DOMXPath;
 
12
  use InvalidArgumentException;
13
  use RuntimeException;
14
 
@@ -20,10 +23,10 @@ class Document
20
  * @const string
21
  */
22
  const TYPE_HTML = 'html';
23
- const TYPE_XML = 'xml';
24
 
25
  /**
26
- * @var \DOMDocument
27
  */
28
  protected $document;
29
 
@@ -38,14 +41,12 @@ class Document
38
  protected $encoding;
39
 
40
  /**
41
- * Constructor.
 
 
 
42
  *
43
- * @param string|null $string An HTML or XML string or a file path
44
- * @param bool $isFile Indicates that the first parameter is a path to a file
45
- * @param string $encoding The document encoding
46
- * @param string $type The document type
47
- *
48
- * @throws \InvalidArgumentException if the passed encoding is not a string
49
  */
50
  public function __construct($string = null, $isFile = false, $encoding = 'UTF-8', $type = Document::TYPE_HTML)
51
  {
@@ -55,7 +56,7 @@ class Document
55
  return;
56
  }
57
 
58
- if (!is_string($encoding)) {
59
  throw new InvalidArgumentException(sprintf('%s expects parameter 3 to be string, %s given', __METHOD__, gettype($encoding)));
60
  }
61
 
@@ -73,12 +74,12 @@ class Document
73
  /**
74
  * Creates a new document.
75
  *
76
- * @param string|null $string An HTML or XML string or a file path
77
- * @param bool $isFile Indicates that the first parameter is a path to a file
78
- * @param string $encoding The document encoding
79
- * @param string $type The document type
80
  *
81
- * @return \DiDom\Document
82
  */
83
  public static function create($string = null, $isFile = false, $encoding = 'UTF-8', $type = Document::TYPE_HTML)
84
  {
@@ -88,11 +89,11 @@ class Document
88
  /**
89
  * Creates a new element node.
90
  *
91
- * @param string $name The tag name of the element
92
- * @param string|null $value The value of the element
93
- * @param array $attributes The attributes of the element
94
  *
95
- * @return \DiDom\Element created element
96
  */
97
  public function createElement($name, $value = null, array $attributes = [])
98
  {
@@ -104,11 +105,13 @@ class Document
104
  /**
105
  * Creates a new element node by CSS selector.
106
  *
107
- * @param string $selector
108
  * @param string|null $value
109
- * @param array $attributes
 
 
110
  *
111
- * @return \DiDom\Element
112
  */
113
  public function createElementBySelector($selector, $value = null, array $attributes = [])
114
  {
@@ -161,20 +164,28 @@ class Document
161
  return new Element(new DOMCdataSection($data));
162
  }
163
 
 
 
 
 
 
 
 
 
164
  /**
165
  * Adds a new child at the end of the children.
166
  *
167
- * @param \DiDom\Element|\DOMNode|array $nodes The appended child
168
  *
169
- * @return \DiDom\Element|\DiDom\Element[]
170
  *
171
- * @throws \InvalidArgumentException if the passed argument is not an instance of \DOMNode or \DiDom\Element
172
  */
173
  public function appendChild($nodes)
174
  {
175
  $returnArray = true;
176
 
177
- if (!is_array($nodes)) {
178
  $nodes = [$nodes];
179
 
180
  $returnArray = false;
@@ -187,7 +198,7 @@ class Document
187
  $node = $node->getNode();
188
  }
189
 
190
- if (!$node instanceof DOMNode) {
191
  throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($node) ? get_class($node) : gettype($node))));
192
  }
193
 
@@ -213,11 +224,11 @@ class Document
213
  *
214
  * @param bool $value
215
  *
216
- * @return \DiDom\Document
217
  */
218
  public function preserveWhiteSpace($value = true)
219
  {
220
- if (!is_bool($value)) {
221
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be boolean, %s given', __METHOD__, gettype($value)));
222
  }
223
 
@@ -229,28 +240,29 @@ class Document
229
  /**
230
  * Load HTML or XML.
231
  *
232
- * @param string $string HTML or XML string or file path
233
- * @param bool $isFile Indicates that in first parameter was passed to the file path
234
- * @param string $type Type of the document
235
- * @param int|null $options Additional parameters
236
  *
237
- * @return \DiDom\Document
238
  *
239
- * @throws \InvalidArgumentException if first parameter is not a string
240
- * @throws \InvalidArgumentException if document type parameter is not a string
241
- * @throws \RuntimeException if document type is not HTML or XML
 
242
  */
243
  public function load($string, $isFile = false, $type = Document::TYPE_HTML, $options = null)
244
  {
245
- if (!is_string($string)) {
246
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($string) ? get_class($string) : gettype($string))));
247
  }
248
 
249
- if (!is_string($type)) {
250
  throw new InvalidArgumentException(sprintf('%s expects parameter 3 to be string, %s given', __METHOD__, (is_object($type) ? get_class($type) : gettype($type))));
251
  }
252
 
253
- if (!in_array(strtolower($type), [Document::TYPE_HTML, Document::TYPE_XML], true)) {
254
  throw new RuntimeException(sprintf('Document type must be "xml" or "html", %s given', $type));
255
  }
256
 
@@ -259,7 +271,7 @@ class Document
259
  $options = LIBXML_HTML_NODEFDTD;
260
  }
261
 
262
- if (!is_int($options)) {
263
  throw new InvalidArgumentException(sprintf('%s expects parameter 4 to be integer, %s given', __METHOD__, (is_object($options) ? get_class($options) : gettype($options))));
264
  }
265
 
@@ -291,12 +303,12 @@ class Document
291
  /**
292
  * Load HTML from a string.
293
  *
294
- * @param string $html The HTML string
295
  * @param int|null $options Additional parameters
296
  *
297
- * @return \DiDom\Document
298
  *
299
- * @throws \InvalidArgumentException if the provided argument is not a string
300
  */
301
  public function loadHtml($html, $options = null)
302
  {
@@ -306,14 +318,14 @@ class Document
306
  /**
307
  * Load HTML from a file.
308
  *
309
- * @param string $filename The path to the HTML file
310
- * @param int|null $options Additional parameters
311
  *
312
- * @return \DiDom\Document
313
  *
314
- * @throws \InvalidArgumentException if the file path is not a string
315
- * @throws \RuntimeException if the file does not exist
316
- * @throws \RuntimeException if you are unable to load the file
317
  */
318
  public function loadHtmlFile($filename, $options = null)
319
  {
@@ -323,12 +335,12 @@ class Document
323
  /**
324
  * Load XML from a string.
325
  *
326
- * @param string $xml The XML string
327
  * @param int|null $options Additional parameters
328
  *
329
- * @return \DiDom\Document
330
  *
331
- * @throws \InvalidArgumentException if the provided argument is not a string
332
  */
333
  public function loadXml($xml, $options = null)
334
  {
@@ -338,14 +350,14 @@ class Document
338
  /**
339
  * Load XML from a file.
340
  *
341
- * @param string $filename The path to the XML file
342
- * @param int|null $options Additional parameters
343
  *
344
- * @return \DiDom\Document
345
  *
346
- * @throws \InvalidArgumentException if the file path is not a string
347
- * @throws \RuntimeException if the file does not exist
348
- * @throws \RuntimeException if you are unable to load the file
349
  */
350
  public function loadXmlFile($filename, $options = null)
351
  {
@@ -359,18 +371,18 @@ class Document
359
  *
360
  * @return string
361
  *
362
- * @throws \InvalidArgumentException if the file path is not a string
363
- * @throws \RuntimeException if an error occurred
364
  */
365
  protected function loadFile($filename)
366
  {
367
- if (!is_string($filename)) {
368
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($filename)));
369
  }
370
 
371
  try {
372
  $content = file_get_contents($filename);
373
- } catch (\Exception $exception) {
374
  throw new RuntimeException(sprintf('Could not load file %s', $filename));
375
  }
376
 
@@ -400,14 +412,15 @@ class Document
400
  /**
401
  * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
402
  *
403
- * @param string $expression XPath expression or a CSS selector
404
- * @param string $type The type of the expression
405
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
406
- * @param \DOMElement|null $contextNode The node in which the search will be performed
407
  *
408
- * @return \DiDom\Element[]|\DOMElement[]
409
  *
410
- * @throws InvalidArgumentException if context node is not \DOMElement
 
411
  */
412
  public function find($expression, $type = Query::TYPE_CSS, $wrapNode = true, $contextNode = null)
413
  {
@@ -418,7 +431,7 @@ class Document
418
  $contextNode = $contextNode->getNode();
419
  }
420
 
421
- if (!$contextNode instanceof DOMElement) {
422
  throw new InvalidArgumentException(sprintf('Argument 4 passed to %s must be an instance of %s\Element or DOMElement, %s given', __METHOD__, __NAMESPACE__, (is_object($contextNode) ? get_class($contextNode) : gettype($contextNode))));
423
  }
424
 
@@ -447,12 +460,14 @@ class Document
447
  /**
448
  * Searches for an node in the DOM tree and returns first element or null.
449
  *
450
- * @param string $expression XPath expression or a CSS selector
451
- * @param string $type The type of the expression
452
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
453
- * @param \DOMElement|null $contextNode The node in which the search will be performed
 
 
454
  *
455
- * @return \DiDom\Element|\DOMElement|null
456
  */
457
  public function first($expression, $type = Query::TYPE_CSS, $wrapNode = true, $contextNode = null)
458
  {
@@ -474,27 +489,23 @@ class Document
474
  }
475
 
476
  /**
477
- * @param \DOMElement|\DOMText|\DOMAttr $node
478
  *
479
- * @return \DiDom\Element|string
480
  *
481
- * @throws InvalidArgumentException if node is not DOMElement, DOMText, DOMComment, DOMCdataSection or DOMAttr
482
  */
483
  protected function wrapNode($node)
484
  {
485
  switch (get_class($node)) {
486
  case 'DOMElement':
 
 
487
  return new Element($node);
488
 
489
  case 'DOMText':
490
  return $node->data;
491
 
492
- case 'DOMComment':
493
- return new Element($node);
494
-
495
- case 'DOMCdataSection':
496
- return new Element($node);
497
-
498
  case 'DOMAttr':
499
  return $node->value;
500
  }
@@ -505,11 +516,11 @@ class Document
505
  /**
506
  * Searches for a node in the DOM tree for a given XPath expression.
507
  *
508
- * @param string $expression XPath expression
509
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
510
- * @param \DOMElement $contextNode The node in which the search will be performed
511
  *
512
- * @return \DiDom\Element[]|\DOMElement[]
513
  */
514
  public function xpath($expression, $wrapNode = true, $contextNode = null)
515
  {
@@ -523,6 +534,8 @@ class Document
523
  * @param string $type The type of the expression
524
  *
525
  * @return int
 
 
526
  */
527
  public function count($expression, $type = Query::TYPE_CSS)
528
  {
@@ -572,11 +585,11 @@ class Document
572
  *
573
  * @param bool $format Formats output if true
574
  *
575
- * @return \DiDom\Document
576
  */
577
  public function format($format = true)
578
  {
579
- if (!is_bool($format)) {
580
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be boolean, %s given', __METHOD__, gettype($format)));
581
  }
582
 
@@ -598,18 +611,18 @@ class Document
598
  /**
599
  * Indicates if two documents are the same document.
600
  *
601
- * @param \DiDom\Document|\DOMDocument $document The compared document
602
  *
603
  * @return bool
604
  *
605
- * @throws \InvalidArgumentException if the provided argument is not an instance of \DOMDocument or \DiDom\Document
606
  */
607
  public function is($document)
608
  {
609
  if ($document instanceof Document) {
610
  $element = $document->getElement();
611
  } else {
612
- if (!$document instanceof DOMDocument) {
613
  throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMDocument, %s given', __METHOD__, __CLASS__, (is_object($document) ? get_class($document) : gettype($document))));
614
  }
615
 
@@ -644,7 +657,7 @@ class Document
644
  }
645
 
646
  /**
647
- * @return \DOMDocument
648
  */
649
  public function getDocument()
650
  {
@@ -652,7 +665,7 @@ class Document
652
  }
653
 
654
  /**
655
- * @return \DOMElement
656
  */
657
  public function getElement()
658
  {
@@ -660,7 +673,7 @@ class Document
660
  }
661
 
662
  /**
663
- * @return \DiDom\Element
664
  */
665
  public function toElement()
666
  {
@@ -684,12 +697,14 @@ class Document
684
  /**
685
  * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
686
  *
687
- * @param string $expression XPath expression or a CSS selector
688
- * @param string $type The type of the expression
689
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
690
- * @param \DOMElement|null $contextNode The node in which the search will be performed
 
 
691
  *
692
- * @return \DiDom\Element[]|\DOMElement[]
693
  *
694
  * @deprecated Not longer recommended, use Document::find() instead.
695
  */
2
 
3
  namespace DiDom;
4
 
5
+ use DiDom\Exceptions\InvalidSelectorException;
6
+ use DOMAttr;
7
  use DOMCdataSection;
8
  use DOMComment;
9
  use DOMDocument;
11
  use DOMNode;
12
  use DOMText;
13
  use DOMXPath;
14
+ use Exception;
15
  use InvalidArgumentException;
16
  use RuntimeException;
17
 
23
  * @const string
24
  */
25
  const TYPE_HTML = 'html';
26
+ const TYPE_XML = 'xml';
27
 
28
  /**
29
+ * @var DOMDocument
30
  */
31
  protected $document;
32
 
41
  protected $encoding;
42
 
43
  /**
44
+ * @param string|null $string An HTML or XML string or a file path
45
+ * @param bool $isFile Indicates that the first parameter is a path to a file
46
+ * @param string $encoding The document encoding
47
+ * @param string $type The document type
48
  *
49
+ * @throws InvalidArgumentException if parameter 3 is not a string
 
 
 
 
 
50
  */
51
  public function __construct($string = null, $isFile = false, $encoding = 'UTF-8', $type = Document::TYPE_HTML)
52
  {
56
  return;
57
  }
58
 
59
+ if ( ! is_string($encoding)) {
60
  throw new InvalidArgumentException(sprintf('%s expects parameter 3 to be string, %s given', __METHOD__, gettype($encoding)));
61
  }
62
 
74
  /**
75
  * Creates a new document.
76
  *
77
+ * @param string|null $string An HTML or XML string or a file path
78
+ * @param bool $isFile Indicates that the first parameter is a path to a file
79
+ * @param string $encoding The document encoding
80
+ * @param string $type The document type
81
  *
82
+ * @return Document
83
  */
84
  public static function create($string = null, $isFile = false, $encoding = 'UTF-8', $type = Document::TYPE_HTML)
85
  {
89
  /**
90
  * Creates a new element node.
91
  *
92
+ * @param string $name The tag name of the element
93
+ * @param string|null $value The value of the element
94
+ * @param array $attributes The attributes of the element
95
  *
96
+ * @return Element created element
97
  */
98
  public function createElement($name, $value = null, array $attributes = [])
99
  {
105
  /**
106
  * Creates a new element node by CSS selector.
107
  *
108
+ * @param string $selector
109
  * @param string|null $value
110
+ * @param array $attributes
111
+ *
112
+ * @return Element
113
  *
114
+ * @throws InvalidSelectorException
115
  */
116
  public function createElementBySelector($selector, $value = null, array $attributes = [])
117
  {
164
  return new Element(new DOMCdataSection($data));
165
  }
166
 
167
+ /**
168
+ * @return DocumentFragment
169
+ */
170
+ public function createDocumentFragment()
171
+ {
172
+ return new DocumentFragment($this->document->createDocumentFragment());
173
+ }
174
+
175
  /**
176
  * Adds a new child at the end of the children.
177
  *
178
+ * @param Element|DOMNode|array $nodes The appended child
179
  *
180
+ * @return Element|Element[]
181
  *
182
+ * @throws InvalidArgumentException if one of elements of parameter 1 is not an instance of DOMNode or Element
183
  */
184
  public function appendChild($nodes)
185
  {
186
  $returnArray = true;
187
 
188
+ if ( ! is_array($nodes)) {
189
  $nodes = [$nodes];
190
 
191
  $returnArray = false;
198
  $node = $node->getNode();
199
  }
200
 
201
+ if ( ! $node instanceof DOMNode) {
202
  throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($node) ? get_class($node) : gettype($node))));
203
  }
204
 
224
  *
225
  * @param bool $value
226
  *
227
+ * @return Document
228
  */
229
  public function preserveWhiteSpace($value = true)
230
  {
231
+ if ( ! is_bool($value)) {
232
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be boolean, %s given', __METHOD__, gettype($value)));
233
  }
234
 
240
  /**
241
  * Load HTML or XML.
242
  *
243
+ * @param string $string An HTML or XML string or a file path
244
+ * @param bool $isFile Indicates that the first parameter is a file path
245
+ * @param string $type The type of a document
246
+ * @param int|null $options libxml option constants
247
  *
248
+ * @return Document
249
  *
250
+ * @throws InvalidArgumentException if parameter 1 is not a string
251
+ * @throws InvalidArgumentException if parameter 3 is not a string
252
+ * @throws InvalidArgumentException if parameter 4 is not an integer or null
253
+ * @throws RuntimeException if the document type is invalid (not Document::TYPE_HTML or Document::TYPE_XML)
254
  */
255
  public function load($string, $isFile = false, $type = Document::TYPE_HTML, $options = null)
256
  {
257
+ if ( ! is_string($string)) {
258
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($string) ? get_class($string) : gettype($string))));
259
  }
260
 
261
+ if ( ! is_string($type)) {
262
  throw new InvalidArgumentException(sprintf('%s expects parameter 3 to be string, %s given', __METHOD__, (is_object($type) ? get_class($type) : gettype($type))));
263
  }
264
 
265
+ if ( ! in_array(strtolower($type), [Document::TYPE_HTML, Document::TYPE_XML], true)) {
266
  throw new RuntimeException(sprintf('Document type must be "xml" or "html", %s given', $type));
267
  }
268
 
271
  $options = LIBXML_HTML_NODEFDTD;
272
  }
273
 
274
+ if ( ! is_int($options)) {
275
  throw new InvalidArgumentException(sprintf('%s expects parameter 4 to be integer, %s given', __METHOD__, (is_object($options) ? get_class($options) : gettype($options))));
276
  }
277
 
303
  /**
304
  * Load HTML from a string.
305
  *
306
+ * @param string $html The HTML string
307
  * @param int|null $options Additional parameters
308
  *
309
+ * @return Document
310
  *
311
+ * @throws InvalidArgumentException if parameter 1 is not a string
312
  */
313
  public function loadHtml($html, $options = null)
314
  {
318
  /**
319
  * Load HTML from a file.
320
  *
321
+ * @param string $filename The path to the HTML file
322
+ * @param int|null $options Additional parameters
323
  *
324
+ * @return Document
325
  *
326
+ * @throws InvalidArgumentException if parameter 1 not a string
327
+ * @throws RuntimeException if the file doesn't exist
328
+ * @throws RuntimeException if you are unable to load the file
329
  */
330
  public function loadHtmlFile($filename, $options = null)
331
  {
335
  /**
336
  * Load XML from a string.
337
  *
338
+ * @param string $xml The XML string
339
  * @param int|null $options Additional parameters
340
  *
341
+ * @return Document
342
  *
343
+ * @throws InvalidArgumentException if parameter 1 is not a string
344
  */
345
  public function loadXml($xml, $options = null)
346
  {
350
  /**
351
  * Load XML from a file.
352
  *
353
+ * @param string $filename The path to the XML file
354
+ * @param int|null $options Additional parameters
355
  *
356
+ * @return Document
357
  *
358
+ * @throws InvalidArgumentException if the file path is not a string
359
+ * @throws RuntimeException if the file doesn't exist
360
+ * @throws RuntimeException if you are unable to load the file
361
  */
362
  public function loadXmlFile($filename, $options = null)
363
  {
371
  *
372
  * @return string
373
  *
374
+ * @throws InvalidArgumentException if parameter 1 is not a string
375
+ * @throws RuntimeException if an error occurred
376
  */
377
  protected function loadFile($filename)
378
  {
379
+ if ( ! is_string($filename)) {
380
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($filename)));
381
  }
382
 
383
  try {
384
  $content = file_get_contents($filename);
385
+ } catch (Exception $exception) {
386
  throw new RuntimeException(sprintf('Could not load file %s', $filename));
387
  }
388
 
412
  /**
413
  * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
414
  *
415
+ * @param string $expression XPath expression or a CSS selector
416
+ * @param string $type The type of the expression
417
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
418
+ * @param DOMElement|null $contextNode The node in which the search will be performed
419
  *
420
+ * @return Element[]|DOMElement[]
421
  *
422
+ * @throws InvalidSelectorException if the selector is invalid
423
+ * @throws InvalidArgumentException if context node is not DOMElement
424
  */
425
  public function find($expression, $type = Query::TYPE_CSS, $wrapNode = true, $contextNode = null)
426
  {
431
  $contextNode = $contextNode->getNode();
432
  }
433
 
434
+ if ( ! $contextNode instanceof DOMElement) {
435
  throw new InvalidArgumentException(sprintf('Argument 4 passed to %s must be an instance of %s\Element or DOMElement, %s given', __METHOD__, __NAMESPACE__, (is_object($contextNode) ? get_class($contextNode) : gettype($contextNode))));
436
  }
437
 
460
  /**
461
  * Searches for an node in the DOM tree and returns first element or null.
462
  *
463
+ * @param string $expression XPath expression or a CSS selector
464
+ * @param string $type The type of the expression
465
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
466
+ * @param DOMElement|null $contextNode The node in which the search will be performed
467
+ *
468
+ * @return Element|DOMElement|null
469
  *
470
+ * @throws InvalidSelectorException if the selector is invalid
471
  */
472
  public function first($expression, $type = Query::TYPE_CSS, $wrapNode = true, $contextNode = null)
473
  {
489
  }
490
 
491
  /**
492
+ * @param DOMElement|DOMText|DOMAttr $node
493
  *
494
+ * @return Element|string
495
  *
496
+ * @throws InvalidArgumentException if parameter 1 is not an instance of DOMElement, DOMText, DOMComment, DOMCdataSection or DOMAttr
497
  */
498
  protected function wrapNode($node)
499
  {
500
  switch (get_class($node)) {
501
  case 'DOMElement':
502
+ case 'DOMComment':
503
+ case 'DOMCdataSection':
504
  return new Element($node);
505
 
506
  case 'DOMText':
507
  return $node->data;
508
 
 
 
 
 
 
 
509
  case 'DOMAttr':
510
  return $node->value;
511
  }
516
  /**
517
  * Searches for a node in the DOM tree for a given XPath expression.
518
  *
519
+ * @param string $expression XPath expression
520
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
521
+ * @param DOMElement $contextNode The node in which the search will be performed
522
  *
523
+ * @return Element[]|DOMElement[]
524
  */
525
  public function xpath($expression, $wrapNode = true, $contextNode = null)
526
  {
534
  * @param string $type The type of the expression
535
  *
536
  * @return int
537
+ *
538
+ * @throws InvalidSelectorException
539
  */
540
  public function count($expression, $type = Query::TYPE_CSS)
541
  {
585
  *
586
  * @param bool $format Formats output if true
587
  *
588
+ * @return Document
589
  */
590
  public function format($format = true)
591
  {
592
+ if ( ! is_bool($format)) {
593
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be boolean, %s given', __METHOD__, gettype($format)));
594
  }
595
 
611
  /**
612
  * Indicates if two documents are the same document.
613
  *
614
+ * @param Document|DOMDocument $document The compared document
615
  *
616
  * @return bool
617
  *
618
+ * @throws InvalidArgumentException if parameter 1 is not an instance of DOMDocument or Document
619
  */
620
  public function is($document)
621
  {
622
  if ($document instanceof Document) {
623
  $element = $document->getElement();
624
  } else {
625
+ if ( ! $document instanceof DOMDocument) {
626
  throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMDocument, %s given', __METHOD__, __CLASS__, (is_object($document) ? get_class($document) : gettype($document))));
627
  }
628
 
657
  }
658
 
659
  /**
660
+ * @return DOMDocument
661
  */
662
  public function getDocument()
663
  {
665
  }
666
 
667
  /**
668
+ * @return DOMElement
669
  */
670
  public function getElement()
671
  {
673
  }
674
 
675
  /**
676
+ * @return Element
677
  */
678
  public function toElement()
679
  {
697
  /**
698
  * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
699
  *
700
+ * @param string $expression XPath expression or a CSS selector
701
+ * @param string $type The type of the expression
702
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
703
+ * @param DOMElement|null $contextNode The node in which the search will be performed
704
+ *
705
+ * @return Element[]|DOMElement[]
706
  *
707
+ * @throws InvalidSelectorException
708
  *
709
  * @deprecated Not longer recommended, use Document::find() instead.
710
  */
vendor/imangazaliev/didom/src/DiDom/DocumentFragment.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DiDom;
4
+
5
+ use DOMDocumentFragment;
6
+ use InvalidArgumentException;
7
+
8
+ /**
9
+ * @property string $tag
10
+ */
11
+ class DocumentFragment extends Node
12
+ {
13
+ /**
14
+ * @param DOMDocumentFragment $documentFragment
15
+ */
16
+ public function __construct($documentFragment)
17
+ {
18
+ if ( ! $documentFragment instanceof DOMDocumentFragment) {
19
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of DOMDocumentFragment, %s given', __METHOD__, (is_object($documentFragment) ? get_class($documentFragment) : gettype($documentFragment))));
20
+ }
21
+
22
+ $this->setNode($documentFragment);
23
+ }
24
+
25
+ /**
26
+ * Append raw XML data.
27
+ *
28
+ * @param string $data
29
+ */
30
+ public function appendXml($data)
31
+ {
32
+ $this->node->appendXML($data);
33
+ }
34
+ }
vendor/imangazaliev/didom/src/DiDom/Element.php CHANGED
@@ -2,6 +2,7 @@
2
 
3
  namespace DiDom;
4
 
 
5
  use DOMCdataSection;
6
  use DOMComment;
7
  use DOMDocument;
@@ -15,31 +16,22 @@ use RuntimeException;
15
  /**
16
  * @property string $tag
17
  */
18
- class Element
19
  {
20
  /**
21
- * The DOM element instance.
22
- *
23
- * @var \DOMElement|\DOMText|\DOMComment|\DOMCdataSection
24
- */
25
- protected $node;
26
-
27
- /**
28
- * @var \DiDom\ClassAttribute
29
  */
30
  protected $classAttribute;
31
 
32
  /**
33
- * @var \DiDom\StyleAttribute
34
  */
35
  protected $styleAttribute;
36
 
37
  /**
38
- * Constructor.
39
- *
40
- * @param \DOMElement|\DOMText|\DOMComment|\DOMCdataSection|string $tagName The tag name of the element
41
- * @param string|null $value The value of the element
42
- * @param array $attributes The attributes of the element
43
  */
44
  public function __construct($tagName, $value = null, array $attributes = [])
45
  {
@@ -65,11 +57,11 @@ class Element
65
  /**
66
  * Creates a new element.
67
  *
68
- * @param \DOMNode|string $name The tag name of the element
69
- * @param string|null $value The value of the element
70
- * @param array $attributes The attributes of the element
71
  *
72
- * @return \DiDom\Element
73
  */
74
  public static function create($name, $value = null, array $attributes = [])
75
  {
@@ -83,290 +75,15 @@ class Element
83
  * @param string|null $value
84
  * @param array $attributes
85
  *
86
- * @return \DiDom\Element
 
 
87
  */
88
  public static function createBySelector($selector, $value = null, array $attributes = [])
89
  {
90
  return Document::create()->createElementBySelector($selector, $value, $attributes);
91
  }
92
 
93
- /**
94
- * Adds a new child at the start of the children.
95
- *
96
- * @param \DiDom\Element|\DOMNode|array $nodes The prepended child
97
- *
98
- * @return \DiDom\Element|\DiDom\Element[]
99
- *
100
- * @throws \LogicException if current node has no owner document
101
- * @throws \InvalidArgumentException if the provided argument is not an instance of \DOMNode or \DiDom\Element
102
- */
103
- public function prependChild($nodes)
104
- {
105
- if ($this->node->ownerDocument === null) {
106
- throw new LogicException('Can not prepend child to element without owner document');
107
- }
108
-
109
- $returnArray = true;
110
-
111
- if (!is_array($nodes)) {
112
- $nodes = [$nodes];
113
-
114
- $returnArray = false;
115
- }
116
-
117
- $nodes = array_reverse($nodes);
118
-
119
- $result = [];
120
-
121
- $referenceNode = $this->node->firstChild;
122
-
123
- foreach ($nodes as $node) {
124
- $result[] = $this->insertBefore($node, $referenceNode);
125
-
126
- $referenceNode = $this->node->firstChild;
127
- }
128
-
129
- return $returnArray ? $result : $result[0];
130
- }
131
-
132
- /**
133
- * Adds a new child at the end of the children.
134
- *
135
- * @param \DiDom\Element|\DOMNode|array $nodes The appended child
136
- *
137
- * @return \DiDom\Element|\DiDom\Element[]
138
- *
139
- * @throws \LogicException if current node has no owner document
140
- * @throws \InvalidArgumentException if the provided argument is not an instance of \DOMNode or \DiDom\Element
141
- */
142
- public function appendChild($nodes)
143
- {
144
- if ($this->node->ownerDocument === null) {
145
- throw new LogicException('Can not append child to element without owner document');
146
- }
147
-
148
- $returnArray = true;
149
-
150
- if (!is_array($nodes)) {
151
- $nodes = [$nodes];
152
-
153
- $returnArray = false;
154
- }
155
-
156
- $result = [];
157
-
158
- Errors::disable();
159
-
160
- foreach ($nodes as $node) {
161
- if ($node instanceof Element) {
162
- $node = $node->getNode();
163
- }
164
-
165
- if (!$node instanceof DOMNode) {
166
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($node) ? get_class($node) : gettype($node))));
167
- }
168
-
169
- $clonedNode = $node->cloneNode(true);
170
- $newNode = $this->node->ownerDocument->importNode($clonedNode, true);
171
-
172
- $result[] = $this->node->appendChild($newNode);
173
- }
174
-
175
- Errors::restore();
176
-
177
- $result = array_map(function (DOMNode $node) {
178
- return new Element($node);
179
- }, $result);
180
-
181
- return $returnArray ? $result : $result[0];
182
- }
183
-
184
- /**
185
- * Adds a new child before a reference node.
186
- *
187
- * @param \DiDom\Element|\DOMNode $node The new node
188
- * @param \DiDom\Element|\DOMNode|null $referenceNode The reference node
189
- *
190
- * @return \DiDom\Element
191
- *
192
- * @throws \LogicException if current node has no owner document
193
- * @throws \InvalidArgumentException if $node is not an instance of \DOMNode or \DiDom\Element
194
- * @throws \InvalidArgumentException if $referenceNode is not an instance of \DOMNode or \DiDom\Element
195
- */
196
- public function insertBefore($node, $referenceNode = null)
197
- {
198
- if ($this->node->ownerDocument === null) {
199
- throw new LogicException('Can not insert child to element without owner document');
200
- }
201
-
202
- if ($node instanceof Element) {
203
- $node = $node->getNode();
204
- }
205
-
206
- if (!$node instanceof DOMNode) {
207
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($node) ? get_class($node) : gettype($node))));
208
- }
209
-
210
- if ($referenceNode !== null) {
211
- if ($referenceNode instanceof Element) {
212
- $referenceNode = $referenceNode->getNode();
213
- }
214
-
215
- if (!$referenceNode instanceof DOMNode) {
216
- throw new InvalidArgumentException(sprintf('Argument 2 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($referenceNode) ? get_class($referenceNode) : gettype($referenceNode))));
217
- }
218
- }
219
-
220
- Errors::disable();
221
-
222
- $clonedNode = $node->cloneNode(true);
223
- $newNode = $this->node->ownerDocument->importNode($clonedNode, true);
224
-
225
- $insertedNode = $this->node->insertBefore($newNode, $referenceNode);
226
-
227
- Errors::restore();
228
-
229
- return new Element($insertedNode);
230
- }
231
-
232
- /**
233
- * Adds a new child after a reference node.
234
- *
235
- * @param \DiDom\Element|\DOMNode $node The new node
236
- * @param \DiDom\Element|\DOMNode|null $referenceNode The reference node
237
- *
238
- * @return \DiDom\Element
239
- *
240
- * @throws \LogicException if current node has no owner document
241
- * @throws \InvalidArgumentException if $node is not an instance of \DOMNode or \DiDom\Element
242
- * @throws \InvalidArgumentException if $referenceNode is not an instance of \DOMNode or \DiDom\Element
243
- */
244
- public function insertAfter($node, $referenceNode = null)
245
- {
246
- if ($referenceNode === null) {
247
- return $this->insertBefore($node);
248
- }
249
-
250
- if ($referenceNode instanceof Element) {
251
- $referenceNode = $referenceNode->getNode();
252
- }
253
-
254
- if (!$referenceNode instanceof DOMNode) {
255
- throw new InvalidArgumentException(sprintf('Argument 2 passed to %s must be an instance of %s\Element or DOMNode, %s given', __METHOD__, __NAMESPACE__, (is_object($referenceNode) ? get_class($referenceNode) : gettype($referenceNode))));
256
- }
257
-
258
- return $this->insertBefore($node, $referenceNode->nextSibling);
259
- }
260
-
261
- /**
262
- * Checks the existence of the node.
263
- *
264
- * @param string $expression XPath expression or CSS selector
265
- * @param string $type The type of the expression
266
- *
267
- * @return bool
268
- */
269
- public function has($expression, $type = Query::TYPE_CSS)
270
- {
271
- return $this->toDocument()->has($expression, $type);
272
- }
273
-
274
- /**
275
- * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
276
- *
277
- * @param string $expression XPath expression or a CSS selector
278
- * @param string $type The type of the expression
279
- * @param bool $wrapElement Returns array of \DiDom\Element if true, otherwise array of \DOMElement
280
- *
281
- * @return \DiDom\Element[]|\DOMElement[]
282
- */
283
- public function find($expression, $type = Query::TYPE_CSS, $wrapElement = true)
284
- {
285
- return $this->toDocument()->find($expression, $type, $wrapElement);
286
- }
287
-
288
- /**
289
- * Searches for an node in the owner document using current node as context.
290
- *
291
- * @param string $expression XPath expression or a CSS selector
292
- * @param string $type The type of the expression
293
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
294
- *
295
- * @return \DiDom\Element[]|\DOMElement[]
296
- *
297
- * @throws \LogicException if current node has no owner document
298
- */
299
- public function findInDocument($expression, $type = Query::TYPE_CSS, $wrapNode = true)
300
- {
301
- $ownerDocument = $this->getDocument();
302
-
303
- if ($ownerDocument === null) {
304
- throw new LogicException('Can not search in context without owner document');
305
- }
306
-
307
- return $ownerDocument->find($expression, $type, $wrapNode, $this->node);
308
- }
309
-
310
- /**
311
- * Searches for an node in the DOM tree and returns first element or null.
312
- *
313
- * @param string $expression XPath expression or a CSS selector
314
- * @param string $type The type of the expression
315
- * @param bool $wrapNode Returns \DiDom\Element if true, otherwise \DOMElement
316
- *
317
- * @return \DiDom\Element|\DOMElement|null
318
- */
319
- public function first($expression, $type = Query::TYPE_CSS, $wrapNode = true)
320
- {
321
- return $this->toDocument()->first($expression, $type, $wrapNode);
322
- }
323
-
324
- /**
325
- * Searches for an node in the owner document using current node as context and returns first element or null.
326
- *
327
- * @param string $expression XPath expression or a CSS selector
328
- * @param string $type The type of the expression
329
- * @param bool $wrapNode Returns \DiDom\Element if true, otherwise \DOMElement
330
- *
331
- * @return \DiDom\Element|\DOMElement|null
332
- */
333
- public function firstInDocument($expression, $type = Query::TYPE_CSS, $wrapNode = true)
334
- {
335
- $ownerDocument = $this->getDocument();
336
-
337
- if ($ownerDocument === null) {
338
- throw new LogicException('Can not search in context without owner document');
339
- }
340
-
341
- return $ownerDocument->first($expression, $type, $wrapNode, $this->node);
342
- }
343
-
344
- /**
345
- * Searches for an node in the DOM tree for a given XPath expression.
346
- *
347
- * @param string $expression XPath expression
348
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
349
- *
350
- * @return \DiDom\Element[]|\DOMElement[]
351
- */
352
- public function xpath($expression, $wrapNode = true)
353
- {
354
- return $this->find($expression, Query::TYPE_XPATH, $wrapNode);
355
- }
356
-
357
- /**
358
- * Counts nodes for a given XPath expression or a CSS selector.
359
- *
360
- * @param string $expression XPath expression or CSS selector
361
- * @param string $type The type of the expression
362
- *
363
- * @return int
364
- */
365
- public function count($expression, $type = Query::TYPE_CSS)
366
- {
367
- return $this->toDocument()->count($expression, $type);
368
- }
369
-
370
  /**
371
  * Checks that the node matches selector.
372
  *
@@ -375,16 +92,17 @@ class Element
375
  *
376
  * @return bool
377
  *
378
- * @throws \InvalidArgumentException if the tag name is not a string
379
- * @throws \RuntimeException if the tag name is not specified in strict mode
 
380
  */
381
  public function matches($selector, $strict = false)
382
  {
383
- if (!is_string($selector)) {
384
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($selector)));
385
  }
386
 
387
- if (!$this->node instanceof DOMElement) {
388
  return false;
389
  }
390
 
@@ -392,7 +110,7 @@ class Element
392
  return true;
393
  }
394
 
395
- if (!$strict) {
396
  $innerHtml = $this->html();
397
  $html = "<root>$innerHtml</root>";
398
 
@@ -407,7 +125,7 @@ class Element
407
 
408
  $segments = Query::getSegments($selector);
409
 
410
- if (!array_key_exists('tag', $segments)) {
411
  throw new RuntimeException(sprintf('Tag name must be specified in %s', $selector));
412
  }
413
 
@@ -452,7 +170,7 @@ class Element
452
  /**
453
  * Determine if an attribute exists on the element.
454
  *
455
- * @param string $name The attribute name
456
  *
457
  * @return bool
458
  */
@@ -464,10 +182,10 @@ class Element
464
  /**
465
  * Set an attribute on the element.
466
  *
467
- * @param string $name The attribute name
468
- * @param string $value The attribute value
469
  *
470
- * @return \DiDom\Element
471
  */
472
  public function setAttribute($name, $value)
473
  {
@@ -475,7 +193,7 @@ class Element
475
  $value = (string) $value;
476
  }
477
 
478
- if (!is_string($value) && $value !== null) {
479
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string or null, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
480
  }
481
 
@@ -487,10 +205,10 @@ class Element
487
  /**
488
  * Access to the element's attributes.
489
  *
490
- * @param string $name The attribute name
491
- * @param string|null $default The value returned if the attribute does not exist
492
  *
493
- * @return string|null The value of the attribute or null if attribute does not exist
494
  */
495
  public function getAttribute($name, $default = null)
496
  {
@@ -504,9 +222,9 @@ class Element
504
  /**
505
  * Unset an attribute on the element.
506
  *
507
- * @param string $name The attribute name
508
  *
509
- * @return \DiDom\Element
510
  */
511
  public function removeAttribute($name)
512
  {
@@ -520,11 +238,11 @@ class Element
520
  *
521
  * @param string[] $exclusions
522
  *
523
- * @return \DiDom\Element
524
  */
525
  public function removeAllAttributes(array $exclusions = [])
526
  {
527
- if (!$this->node instanceof DOMElement) {
528
  return $this;
529
  }
530
 
@@ -542,10 +260,10 @@ class Element
542
  /**
543
  * Alias for getAttribute and setAttribute methods.
544
  *
545
- * @param string $name The attribute name
546
- * @param string|null $value The attribute value or null if the attribute does not exist
547
  *
548
- * @return string|null|\DiDom\Element
549
  */
550
  public function attr($name, $value = null)
551
  {
@@ -565,7 +283,7 @@ class Element
565
  */
566
  public function attributes(array $names = null)
567
  {
568
- if (!$this->node instanceof DOMElement) {
569
  return null;
570
  }
571
 
@@ -591,9 +309,9 @@ class Element
591
  }
592
 
593
  /**
594
- * @return \DiDom\ClassAttribute
595
  *
596
- * @throws \LogicException if the node is not an instance of \DOMElement
597
  */
598
  public function classes()
599
  {
@@ -601,7 +319,7 @@ class Element
601
  return $this->classAttribute;
602
  }
603
 
604
- if (!$this->isElementNode()) {
605
  throw new LogicException('Class attribute is available only for element nodes');
606
  }
607
 
@@ -611,9 +329,9 @@ class Element
611
  }
612
 
613
  /**
614
- * @return \DiDom\StyleAttribute
615
  *
616
- * @throws \LogicException if the node is not an instance of \DOMElement
617
  */
618
  public function style()
619
  {
@@ -621,7 +339,7 @@ class Element
621
  return $this->styleAttribute;
622
  }
623
 
624
- if (!$this->isElementNode()) {
625
  throw new LogicException('Style attribute is available only for element nodes');
626
  }
627
 
@@ -631,847 +349,53 @@ class Element
631
  }
632
 
633
  /**
634
- * Dumps the node into a string using HTML formatting (including child nodes).
635
- *
636
- * @return string
637
- */
638
- public function html()
639
- {
640
- return $this->toDocument()->html();
641
- }
642
-
643
- /**
644
- * Dumps the node into a string using HTML formatting (without child nodes).
645
- *
646
- * @return string
647
- */
648
- public function outerHtml()
649
- {
650
- $document = new DOMDocument();
651
-
652
- $importedNode = $document->importNode($this->node);
653
-
654
- return $document->saveHTML($importedNode);
655
- }
656
-
657
- /**
658
- * Dumps the node descendants into a string using HTML formatting.
659
- *
660
- * @param string $delimiter
661
- *
662
- * @return string
663
- */
664
- public function innerHtml($delimiter = '')
665
- {
666
- $innerHtml = [];
667
-
668
- foreach ($this->node->childNodes as $childNode) {
669
- $innerHtml[] = $childNode->ownerDocument->saveHTML($childNode);
670
- }
671
-
672
- return implode($delimiter, $innerHtml);
673
- }
674
-
675
- /**
676
- * Dumps the node descendants into a string using XML formatting.
677
- *
678
- * @param string $delimiter
679
- *
680
- * @return string
681
- */
682
- public function innerXml($delimiter = '')
683
- {
684
- $innerXml = [];
685
-
686
- foreach ($this->node->childNodes as $childNode) {
687
- $innerXml[] = $childNode->ownerDocument->saveXML($childNode);
688
- }
689
-
690
- return implode($delimiter, $innerXml);
691
- }
692
-
693
- /**
694
- * Sets inner HTML.
695
- *
696
- * @param string $html
697
- *
698
- * @return \DiDom\Element
699
- *
700
- * @throws InvalidArgumentException if passed argument is not a string
701
- */
702
- public function setInnerHtml($html)
703
- {
704
- if (!is_string($html)) {
705
- throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($html) ? get_class($html) : gettype($html))));
706
- }
707
-
708
- $this->removeChildren();
709
-
710
- if ($html !== '') {
711
- Errors::disable();
712
-
713
- $html = "<htmlfragment>$html</htmlfragment>";
714
-
715
- $document = new Document($html);
716
-
717
- $fragment = $document->first('htmlfragment')->getNode();
718
-
719
- foreach ($fragment->childNodes as $node) {
720
- $newNode = $this->node->ownerDocument->importNode($node, true);
721
-
722
- $this->node->appendChild($newNode);
723
- }
724
-
725
- Errors::restore();
726
- }
727
-
728
- return $this;
729
- }
730
-
731
- /**
732
- * Dumps the node into a string using XML formatting.
733
- *
734
- * @param int $options Additional options
735
  *
736
- * @return string The node XML
737
- */
738
- public function xml($options = 0)
739
- {
740
- return $this->toDocument()->xml($options);
741
- }
742
-
743
- /**
744
- * Get the text content of this node and its descendants.
745
  *
746
- * @return string The node value
747
  */
748
- public function text()
749
  {
750
- return $this->node->textContent;
751
  }
752
 
753
  /**
754
- * Set the value of this node.
755
- *
756
- * @param string $value The new value of the node
757
  *
758
- * @return \DiDom\Element
759
  *
760
- * @throws \InvalidArgumentException if value is not string
761
  */
762
- public function setValue($value)
763
  {
764
- if (is_numeric($value)) {
765
- $value = (string) $value;
766
- }
767
-
768
- if (!is_string($value) && $value !== null) {
769
- throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
770
  }
771
 
772
- $this->node->nodeValue = $value;
773
-
774
- return $this;
775
- }
776
-
777
- /**
778
- * Returns true if current node is a DOMElement instance.
779
- *
780
- * @return bool
781
- */
782
- public function isElementNode()
783
- {
784
- return $this->node instanceof DOMElement;
785
  }
786
 
787
  /**
788
- * Returns true if current node is a a DOMText instance.
789
  *
790
- * @return bool
791
- */
792
- public function isTextNode()
793
- {
794
- return $this->node instanceof DOMText;
795
- }
796
-
797
- /**
798
- * Returns true if current node is a DOMComment instance.
799
  *
800
  * @return bool
801
  */
802
- public function isCommentNode()
803
  {
804
- return $this->node instanceof DOMComment;
805
  }
806
 
807
  /**
808
- * Returns true if current node is a DOMCdataSection instance.
809
  *
810
- * @return bool
811
  */
812
- public function isCdataSectionNode()
813
- {
814
- return $this->node instanceof DOMCdataSection;
815
- }
816
-
817
- /**
818
- * Indicates if two nodes are the same node.
819
- *
820
- * @param \DiDom\Element|\DOMNode $node
821
- *
822
- * @return bool
823
- *
824
- * @throws \InvalidArgumentException if the provided argument is not an instance of \DOMNode
825
- */
826
- public function is($node)
827
- {
828
- if ($node instanceof Element) {
829
- $node = $node->getNode();
830
- }
831
-
832
- if (!$node instanceof DOMNode) {
833
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($node) ? get_class($node) : gettype($node))));
834
- }
835
-
836
- return $this->node->isSameNode($node);
837
- }
838
-
839
- /**
840
- * @return \DiDom\Element|\DiDom\Document|null
841
- */
842
- public function parent()
843
- {
844
- if ($this->node->parentNode === null) {
845
- return null;
846
- }
847
-
848
- if ($this->node->parentNode instanceof DOMDocument) {
849
- return new Document($this->node->parentNode);
850
- }
851
-
852
- return new Element($this->node->parentNode);
853
- }
854
-
855
- /**
856
- * Returns first parent node matches passed selector.
857
- *
858
- * @param string $selector
859
- * @param bool $strict
860
- *
861
- * @return \DiDom\Element|null
862
- */
863
- public function closest($selector, $strict = false)
864
- {
865
- $node = $this;
866
-
867
- while (true) {
868
- $parent = $node->parent();
869
-
870
- if ($parent === null || $parent instanceof Document) {
871
- return null;
872
- }
873
-
874
- if ($parent->matches($selector, $strict)) {
875
- return $parent;
876
- }
877
-
878
- $node = $parent;
879
- }
880
- }
881
-
882
- /**
883
- * @param string|null $selector
884
- * @param string|null $nodeType
885
- *
886
- * @return \DiDom\Element|null
887
- *
888
- * @throws \InvalidArgumentException if node type is not string
889
- * @throws \RuntimeException if node type is invalid
890
- * @throws \LogicException if selector used with non DOMElement node type
891
- */
892
- public function previousSibling($selector = null, $nodeType = null)
893
- {
894
- if ($this->node->previousSibling === null) {
895
- return null;
896
- }
897
-
898
- if ($selector === null && $nodeType === null) {
899
- return new Element($this->node->previousSibling);
900
- }
901
-
902
- if ($selector !== null && $nodeType === null) {
903
- $nodeType = 'DOMElement';
904
- }
905
-
906
- if (!is_string($nodeType)) {
907
- throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
908
- }
909
-
910
- $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
911
-
912
- if (!in_array($nodeType, $allowedTypes, true)) {
913
- throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
914
- }
915
-
916
- if ($selector !== null && $nodeType !== 'DOMElement') {
917
- throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
918
- }
919
-
920
- $node = $this->node->previousSibling;
921
-
922
- while ($node !== null) {
923
- if (get_class($node) !== $nodeType) {
924
- $node = $node->previousSibling;
925
-
926
- continue;
927
- }
928
-
929
- $element = new Element($node);
930
-
931
- if ($selector === null) {
932
- return $element;
933
- }
934
-
935
- if ($element->matches($selector)) {
936
- return $element;
937
- }
938
-
939
- $node = $node->previousSibling;
940
- }
941
-
942
- return new Element($this->node->previousSibling);
943
- }
944
-
945
- /**
946
- * @param string|null $selector
947
- * @param string|null $nodeType
948
- *
949
- * @return \DiDom\Element[]
950
- *
951
- * @throws \InvalidArgumentException if node type is not string
952
- * @throws \RuntimeException if node type is invalid
953
- * @throws \LogicException if selector used with non DOMElement node type
954
- */
955
- public function previousSiblings($selector = null, $nodeType = null)
956
- {
957
- if ($this->node->previousSibling === null) {
958
- return [];
959
- }
960
-
961
- if ($selector !== null && $nodeType === null) {
962
- $nodeType = 'DOMElement';
963
- }
964
-
965
- if ($nodeType !== null) {
966
- if (!is_string($nodeType)) {
967
- throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
968
- }
969
-
970
- $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
971
-
972
- if (!in_array($nodeType, $allowedTypes, true)) {
973
- throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
974
- }
975
- }
976
-
977
- if ($selector !== null && $nodeType !== 'DOMElement') {
978
- throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
979
- }
980
-
981
- $result = [];
982
-
983
- $node = $this->node->previousSibling;
984
-
985
- while ($node !== null) {
986
- $element = new Element($node);
987
-
988
- if ($nodeType === null) {
989
- $result[] = $element;
990
-
991
- $node = $node->previousSibling;
992
-
993
- continue;
994
- }
995
-
996
- if (get_class($node) !== $nodeType) {
997
- $node = $node->previousSibling;
998
-
999
- continue;
1000
- }
1001
-
1002
- if ($selector === null) {
1003
- $result[] = $element;
1004
-
1005
- $node = $node->previousSibling;
1006
-
1007
- continue;
1008
- }
1009
-
1010
- if ($element->matches($selector)) {
1011
- $result[] = $element;
1012
- }
1013
-
1014
- $node = $node->previousSibling;
1015
- }
1016
-
1017
- return array_reverse($result);
1018
- }
1019
-
1020
- /**
1021
- * @param string|null $selector
1022
- * @param string|null $nodeType
1023
- *
1024
- * @return \DiDom\Element|null
1025
- *
1026
- * @throws \InvalidArgumentException if node type is not string
1027
- * @throws \RuntimeException if node type is invalid
1028
- * @throws \LogicException if selector used with non DOMElement node type
1029
- */
1030
- public function nextSibling($selector = null, $nodeType = null)
1031
- {
1032
- if ($this->node->nextSibling === null) {
1033
- return null;
1034
- }
1035
-
1036
- if ($selector === null && $nodeType === null) {
1037
- return new Element($this->node->nextSibling);
1038
- }
1039
-
1040
- if ($selector !== null && $nodeType === null) {
1041
- $nodeType = 'DOMElement';
1042
- }
1043
-
1044
- if (!is_string($nodeType)) {
1045
- throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
1046
- }
1047
-
1048
- $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
1049
-
1050
- if (!in_array($nodeType, $allowedTypes, true)) {
1051
- throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
1052
- }
1053
-
1054
- if ($selector !== null && $nodeType !== 'DOMElement') {
1055
- throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
1056
- }
1057
-
1058
- $node = $this->node->nextSibling;
1059
-
1060
- while ($node !== null) {
1061
- if (get_class($node) !== $nodeType) {
1062
- $node = $node->nextSibling;
1063
-
1064
- continue;
1065
- }
1066
-
1067
- $element = new Element($node);
1068
-
1069
- if ($selector === null) {
1070
- return $element;
1071
- }
1072
-
1073
- if ($element->matches($selector)) {
1074
- return $element;
1075
- }
1076
-
1077
- $node = $node->nextSibling;
1078
- }
1079
-
1080
- return null;
1081
- }
1082
-
1083
- /**
1084
- * @param string|null $selector
1085
- * @param string $nodeType
1086
- *
1087
- * @return \DiDom\Element[]
1088
- *
1089
- * @throws \InvalidArgumentException if node type is not string
1090
- * @throws \RuntimeException if node type is invalid
1091
- * @throws \LogicException if selector used with non DOMElement node type
1092
- */
1093
- public function nextSiblings($selector = null, $nodeType = null)
1094
- {
1095
- if ($this->node->nextSibling === null) {
1096
- return [];
1097
- }
1098
-
1099
- if ($selector !== null && $nodeType === null) {
1100
- $nodeType = 'DOMElement';
1101
- }
1102
-
1103
- if ($nodeType !== null) {
1104
- if (!is_string($nodeType)) {
1105
- throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
1106
- }
1107
-
1108
- $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
1109
-
1110
- if (!in_array($nodeType, $allowedTypes, true)) {
1111
- throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
1112
- }
1113
- }
1114
-
1115
- if ($selector !== null && $nodeType !== 'DOMElement') {
1116
- throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
1117
- }
1118
-
1119
- $result = [];
1120
-
1121
- $node = $this->node->nextSibling;
1122
-
1123
- while ($node !== null) {
1124
- $element = new Element($node);
1125
-
1126
- if ($nodeType === null) {
1127
- $result[] = $element;
1128
-
1129
- $node = $node->nextSibling;
1130
-
1131
- continue;
1132
- }
1133
-
1134
- if (get_class($node) !== $nodeType) {
1135
- $node = $node->nextSibling;
1136
-
1137
- continue;
1138
- }
1139
-
1140
- if ($selector === null) {
1141
- $result[] = $element;
1142
-
1143
- $node = $node->nextSibling;
1144
-
1145
- continue;
1146
- }
1147
-
1148
- if ($element->matches($selector)) {
1149
- $result[] = $element;
1150
- }
1151
-
1152
- $node = $node->nextSibling;
1153
- }
1154
-
1155
- return $result;
1156
- }
1157
-
1158
- /**
1159
- * @param int $index
1160
- *
1161
- * @return \DiDom\Element|null
1162
- */
1163
- public function child($index)
1164
- {
1165
- $child = $this->node->childNodes->item($index);
1166
-
1167
- return $child === null ? null : new Element($child);
1168
- }
1169
-
1170
- /**
1171
- * @return \DiDom\Element|null
1172
- */
1173
- public function firstChild()
1174
- {
1175
- if ($this->node->firstChild === null) {
1176
- return null;
1177
- }
1178
-
1179
- return new Element($this->node->firstChild);
1180
- }
1181
-
1182
- /**
1183
- * @return \DiDom\Element|null
1184
- */
1185
- public function lastChild()
1186
- {
1187
- if ($this->node->lastChild === null) {
1188
- return null;
1189
- }
1190
-
1191
- return new Element($this->node->lastChild);
1192
- }
1193
-
1194
- /**
1195
- * @return bool
1196
- */
1197
- public function hasChildren()
1198
- {
1199
- return $this->node->hasChildNodes();
1200
- }
1201
-
1202
- /**
1203
- * @return \DiDom\Element[]
1204
- */
1205
- public function children()
1206
- {
1207
- $children = [];
1208
-
1209
- foreach ($this->node->childNodes as $node) {
1210
- $children[] = new Element($node);
1211
- }
1212
-
1213
- return $children;
1214
- }
1215
-
1216
- /**
1217
- * Removes child from list of children.
1218
- *
1219
- * @param \DOMNode|\DiDom\Element $childNode
1220
- *
1221
- * @return \DiDom\Element the node that has been removed
1222
- */
1223
- public function removeChild($childNode)
1224
- {
1225
- if ($childNode instanceof Element) {
1226
- $childNode = $childNode->getNode();
1227
- }
1228
-
1229
- if (!$childNode instanceof DOMNode) {
1230
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($childNode) ? get_class($childNode) : gettype($childNode))));
1231
- }
1232
-
1233
- $removedNode = $this->node->removeChild($childNode);
1234
-
1235
- return new Element($removedNode);
1236
- }
1237
-
1238
- /**
1239
- * Removes all child nodes.
1240
- *
1241
- * @return \DiDom\Element[] the nodes that has been removed
1242
- */
1243
- public function removeChildren()
1244
- {
1245
- // we need to collect child nodes to array
1246
- // because removing nodes from the DOMNodeList on iterating is not working
1247
- $childNodes = [];
1248
-
1249
- foreach ($this->node->childNodes as $childNode) {
1250
- $childNodes[] = $childNode;
1251
- }
1252
-
1253
- $removedNodes = [];
1254
-
1255
- foreach ($childNodes as $childNode) {
1256
- $removedNode = $this->node->removeChild($childNode);
1257
-
1258
- $removedNodes[] = new Element($removedNode);
1259
- }
1260
-
1261
- return $removedNodes;
1262
- }
1263
-
1264
- /**
1265
- * Removes current node from the parent.
1266
- *
1267
- * @return \DiDom\Element the node that has been removed
1268
- *
1269
- * @throws \LogicException if current node has no parent node
1270
- */
1271
- public function remove()
1272
- {
1273
- if ($this->node->parentNode === null) {
1274
- throw new LogicException('Can not remove element without parent node');
1275
- }
1276
-
1277
- $removedNode = $this->node->parentNode->removeChild($this->node);
1278
-
1279
- return new Element($removedNode);
1280
- }
1281
-
1282
- /**
1283
- * Replaces a child.
1284
- *
1285
- * @param \DOMNode|\DiDom\Element $newNode The new node
1286
- * @param bool $clone Clone the node if true, otherwise move it
1287
- *
1288
- * @return \DiDom\Element The node that has been replaced
1289
- *
1290
- * @throws \LogicException if current node has no parent node
1291
- */
1292
- public function replace($newNode, $clone = true)
1293
- {
1294
- if ($this->node->parentNode === null) {
1295
- throw new LogicException('Can not replace element without parent node');
1296
- }
1297
-
1298
- if ($newNode instanceof Element) {
1299
- $newNode = $newNode->getNode();
1300
- }
1301
-
1302
- if (!$newNode instanceof DOMNode) {
1303
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($newNode) ? get_class($newNode) : gettype($newNode))));
1304
- }
1305
-
1306
- if ($clone) {
1307
- $newNode = $newNode->cloneNode(true);
1308
- }
1309
-
1310
- if ($newNode->ownerDocument === null || !$this->getDocument()->is($newNode->ownerDocument)) {
1311
- $newNode = $this->node->ownerDocument->importNode($newNode, true);
1312
- }
1313
-
1314
- $node = $this->node->parentNode->replaceChild($newNode, $this->node);
1315
-
1316
- return new Element($node);
1317
- }
1318
-
1319
- /**
1320
- * Get line number for a node.
1321
- *
1322
- * @return int
1323
- */
1324
- public function getLineNo()
1325
- {
1326
- return $this->node->getLineNo();
1327
- }
1328
-
1329
- /**
1330
- * Clones a node.
1331
- *
1332
- * @param bool $deep Indicates whether to copy all descendant nodes
1333
- *
1334
- * @return \DiDom\Element The cloned node
1335
- */
1336
- public function cloneNode($deep = true)
1337
- {
1338
- return new Element($this->node->cloneNode($deep));
1339
- }
1340
-
1341
- /**
1342
- * Sets current node instance.
1343
- *
1344
- * @param \DOMElement|\DOMText|\DOMComment|\DOMCdataSection $node
1345
- *
1346
- * @return \DiDom\Element
1347
- */
1348
- protected function setNode($node)
1349
- {
1350
- $allowedClasses = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
1351
-
1352
- if (!is_object($node) || !in_array(get_class($node), $allowedClasses, true)) {
1353
- throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of DOMElement, DOMText, DOMComment or DOMCdataSection, %s given', __METHOD__, (is_object($node) ? get_class($node) : gettype($node))));
1354
- }
1355
-
1356
- $this->node = $node;
1357
-
1358
- return $this;
1359
- }
1360
-
1361
- /**
1362
- * Returns current node instance.
1363
- *
1364
- * @return \DOMElement|\DOMText|\DOMComment|\DOMCdataSection
1365
- */
1366
- public function getNode()
1367
- {
1368
- return $this->node;
1369
- }
1370
-
1371
- /**
1372
- * Returns the document associated with this node.
1373
- *
1374
- * @return \DiDom\Document|null
1375
- */
1376
- public function getDocument()
1377
- {
1378
- if ($this->node->ownerDocument === null) {
1379
- return null;
1380
- }
1381
-
1382
- return new Document($this->node->ownerDocument);
1383
- }
1384
-
1385
- /**
1386
- * Get the DOM document with the current element.
1387
- *
1388
- * @param string $encoding The document encoding
1389
- *
1390
- * @return \DiDom\Document
1391
- */
1392
- public function toDocument($encoding = 'UTF-8')
1393
- {
1394
- $document = new Document(null, false, $encoding);
1395
-
1396
- $document->appendChild($this->node);
1397
-
1398
- return $document;
1399
- }
1400
-
1401
- /**
1402
- * Dynamically set an attribute on the element.
1403
- *
1404
- * @param string $name The attribute name
1405
- * @param mixed $value The attribute value
1406
- *
1407
- * @return \DiDom\Element
1408
- */
1409
- public function __set($name, $value)
1410
- {
1411
- return $this->setAttribute($name, $value);
1412
- }
1413
-
1414
- /**
1415
- * Dynamically access the element's attributes.
1416
- *
1417
- * @param string $name The attribute name
1418
- *
1419
- * @return string|null
1420
- */
1421
- public function __get($name)
1422
- {
1423
- if ($name === 'tag') {
1424
- return $this->node->tagName;
1425
- }
1426
-
1427
- return $this->getAttribute($name);
1428
- }
1429
-
1430
- /**
1431
- * Determine if an attribute exists on the element.
1432
- *
1433
- * @param string $name The attribute name
1434
- *
1435
- * @return bool
1436
- */
1437
- public function __isset($name)
1438
- {
1439
- return $this->hasAttribute($name);
1440
- }
1441
-
1442
- /**
1443
- * Unset an attribute on the model.
1444
- *
1445
- * @param string $name The attribute name
1446
- */
1447
- public function __unset($name)
1448
  {
1449
  $this->removeAttribute($name);
1450
  }
1451
-
1452
- /**
1453
- * Convert the element to its string representation.
1454
- *
1455
- * @return string
1456
- */
1457
- public function __toString()
1458
- {
1459
- return $this->html();
1460
- }
1461
-
1462
- /**
1463
- * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
1464
- *
1465
- * @param string $expression XPath expression or a CSS selector
1466
- * @param string $type The type of the expression
1467
- * @param bool $wrapNode Returns array of \DiDom\Element if true, otherwise array of \DOMElement
1468
- *
1469
- * @return \DiDom\Element[]|\DOMElement[]
1470
- *
1471
- * @deprecated Not longer recommended, use Element::find() instead.
1472
- */
1473
- public function __invoke($expression, $type = Query::TYPE_CSS, $wrapNode = true)
1474
- {
1475
- return $this->find($expression, $type, $wrapNode);
1476
- }
1477
  }
2
 
3
  namespace DiDom;
4
 
5
+ use DiDom\Exceptions\InvalidSelectorException;
6
  use DOMCdataSection;
7
  use DOMComment;
8
  use DOMDocument;
16
  /**
17
  * @property string $tag
18
  */
19
+ class Element extends Node
20
  {
21
  /**
22
+ * @var ClassAttribute
 
 
 
 
 
 
 
23
  */
24
  protected $classAttribute;
25
 
26
  /**
27
+ * @var StyleAttribute
28
  */
29
  protected $styleAttribute;
30
 
31
  /**
32
+ * @param DOMElement|DOMText|DOMComment|DOMCdataSection|string $tagName The tag name of an element
33
+ * @param string|null $value The value of an element
34
+ * @param array $attributes The attributes of an element
 
 
35
  */
36
  public function __construct($tagName, $value = null, array $attributes = [])
37
  {
57
  /**
58
  * Creates a new element.
59
  *
60
+ * @param DOMNode|string $name The tag name of an element
61
+ * @param string|null $value The value of an element
62
+ * @param array $attributes The attributes of an element
63
  *
64
+ * @return Element
65
  */
66
  public static function create($name, $value = null, array $attributes = [])
67
  {
75
  * @param string|null $value
76
  * @param array $attributes
77
  *
78
+ * @return Element
79
+ *
80
+ * @throws InvalidSelectorException
81
  */
82
  public static function createBySelector($selector, $value = null, array $attributes = [])
83
  {
84
  return Document::create()->createElementBySelector($selector, $value, $attributes);
85
  }
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  /**
88
  * Checks that the node matches selector.
89
  *
92
  *
93
  * @return bool
94
  *
95
+ * @throws InvalidSelectorException if the selector is invalid
96
+ * @throws InvalidArgumentException if the tag name is not a string
97
+ * @throws RuntimeException if the tag name is not specified in strict mode
98
  */
99
  public function matches($selector, $strict = false)
100
  {
101
+ if ( ! is_string($selector)) {
102
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($selector)));
103
  }
104
 
105
+ if ( ! $this->node instanceof DOMElement) {
106
  return false;
107
  }
108
 
110
  return true;
111
  }
112
 
113
+ if ( ! $strict) {
114
  $innerHtml = $this->html();
115
  $html = "<root>$innerHtml</root>";
116
 
125
 
126
  $segments = Query::getSegments($selector);
127
 
128
+ if ( ! array_key_exists('tag', $segments)) {
129
  throw new RuntimeException(sprintf('Tag name must be specified in %s', $selector));
130
  }
131
 
170
  /**
171
  * Determine if an attribute exists on the element.
172
  *
173
+ * @param string $name The name of an attribute
174
  *
175
  * @return bool
176
  */
182
  /**
183
  * Set an attribute on the element.
184
  *
185
+ * @param string $name The name of an attribute
186
+ * @param string $value The value of an attribute
187
  *
188
+ * @return Element
189
  */
190
  public function setAttribute($name, $value)
191
  {
193
  $value = (string) $value;
194
  }
195
 
196
+ if ( ! is_string($value) && $value !== null) {
197
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string or null, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
198
  }
199
 
205
  /**
206
  * Access to the element's attributes.
207
  *
208
+ * @param string $name The name of an attribute
209
+ * @param string|null $default The value returned if the attribute doesn't exist
210
  *
211
+ * @return string|null The value of an attribute or null if attribute doesn't exist
212
  */
213
  public function getAttribute($name, $default = null)
214
  {
222
  /**
223
  * Unset an attribute on the element.
224
  *
225
+ * @param string $name The name of an attribute
226
  *
227
+ * @return Element
228
  */
229
  public function removeAttribute($name)
230
  {
238
  *
239
  * @param string[] $exclusions
240
  *
241
+ * @return Element
242
  */
243
  public function removeAllAttributes(array $exclusions = [])
244
  {
245
+ if ( ! $this->node instanceof DOMElement) {
246
  return $this;
247
  }
248
 
260
  /**
261
  * Alias for getAttribute and setAttribute methods.
262
  *
263
+ * @param string $name The name of an attribute
264
+ * @param string|null $value The value that will be returned an attribute doesn't exist
265
  *
266
+ * @return string|null|Element
267
  */
268
  public function attr($name, $value = null)
269
  {
283
  */
284
  public function attributes(array $names = null)
285
  {
286
+ if ( ! $this->node instanceof DOMElement) {
287
  return null;
288
  }
289
 
309
  }
310
 
311
  /**
312
+ * @return ClassAttribute
313
  *
314
+ * @throws LogicException if the node is not an instance of DOMElement
315
  */
316
  public function classes()
317
  {
319
  return $this->classAttribute;
320
  }
321
 
322
+ if ( ! $this->isElementNode()) {
323
  throw new LogicException('Class attribute is available only for element nodes');
324
  }
325
 
329
  }
330
 
331
  /**
332
+ * @return StyleAttribute
333
  *
334
+ * @throws LogicException if the node is not an instance of DOMElement
335
  */
336
  public function style()
337
  {
339
  return $this->styleAttribute;
340
  }
341
 
342
+ if ( ! $this->isElementNode()) {
343
  throw new LogicException('Style attribute is available only for element nodes');
344
  }
345
 
349
  }
350
 
351
  /**
352
+ * Dynamically set an attribute on the element.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
  *
354
+ * @param string $name The name of an attribute
355
+ * @param string $value The value of an attribute
 
 
 
 
 
 
 
356
  *
357
+ * @return Element
358
  */
359
+ public function __set($name, $value)
360
  {
361
+ return $this->setAttribute($name, $value);
362
  }
363
 
364
  /**
365
+ * Dynamically access the element's attributes.
 
 
366
  *
367
+ * @param string $name The name of an attribute
368
  *
369
+ * @return string|null
370
  */
371
+ public function __get($name)
372
  {
373
+ if ($name === 'tag') {
374
+ return $this->node->tagName;
 
 
 
 
375
  }
376
 
377
+ return $this->getAttribute($name);
 
 
 
 
 
 
 
 
 
 
 
 
378
  }
379
 
380
  /**
381
+ * Determine if an attribute exists on the element.
382
  *
383
+ * @param string $name The attribute name
 
 
 
 
 
 
 
 
384
  *
385
  * @return bool
386
  */
387
+ public function __isset($name)
388
  {
389
+ return $this->hasAttribute($name);
390
  }
391
 
392
  /**
393
+ * Unset an attribute on the model.
394
  *
395
+ * @param string $name The name of an attribute
396
  */
397
+ public function __unset($name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  {
399
  $this->removeAttribute($name);
400
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  }
vendor/imangazaliev/didom/src/DiDom/Node.php ADDED
@@ -0,0 +1,1193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace DiDom;
4
+
5
+ use DiDom\Exceptions\InvalidSelectorException;
6
+ use DOMCdataSection;
7
+ use DOMComment;
8
+ use DOMDocument;
9
+ use DOMDocumentFragment;
10
+ use DOMElement;
11
+ use DOMNode;
12
+ use DOMText;
13
+ use InvalidArgumentException;
14
+ use LogicException;
15
+ use RuntimeException;
16
+
17
+ /**
18
+ * @property string $tag
19
+ */
20
+ abstract class Node
21
+ {
22
+ /**
23
+ * The DOM element instance.
24
+ *
25
+ * @var DOMElement|DOMText|DOMComment|DOMCdataSection|DOMDocumentFragment
26
+ */
27
+ protected $node;
28
+
29
+ /**
30
+ * Adds a new child at the start of the children.
31
+ *
32
+ * @param Node|DOMNode|array $nodes The prepended child
33
+ *
34
+ * @return Element|Element[]
35
+ *
36
+ * @throws LogicException if the current node has no owner document
37
+ * @throws InvalidArgumentException if one of elements of parameter 1 is not an instance of DOMNode or Element
38
+ */
39
+ public function prependChild($nodes)
40
+ {
41
+ if ($this->node->ownerDocument === null) {
42
+ throw new LogicException('Can not prepend a child to element without the owner document');
43
+ }
44
+
45
+ $returnArray = true;
46
+
47
+ if ( ! is_array($nodes)) {
48
+ $nodes = [$nodes];
49
+
50
+ $returnArray = false;
51
+ }
52
+
53
+ $nodes = array_reverse($nodes);
54
+
55
+ $result = [];
56
+
57
+ $referenceNode = $this->node->firstChild;
58
+
59
+ foreach ($nodes as $node) {
60
+ $result[] = $this->insertBefore($node, $referenceNode);
61
+
62
+ $referenceNode = $this->node->firstChild;
63
+ }
64
+
65
+ return $returnArray ? $result : $result[0];
66
+ }
67
+
68
+ /**
69
+ * Adds a new child at the end of the children.
70
+ *
71
+ * @param Node|DOMNode|array $nodes The appended child
72
+ *
73
+ * @return Element|Element[]
74
+ *
75
+ * @throws LogicException if the current node has no owner document
76
+ * @throws InvalidArgumentException if the provided argument is not an instance of DOMNode or Element
77
+ */
78
+ public function appendChild($nodes)
79
+ {
80
+ if ($this->node->ownerDocument === null) {
81
+ throw new LogicException('Can not append a child to element without the owner document');
82
+ }
83
+
84
+ $returnArray = true;
85
+
86
+ if ( ! is_array($nodes)) {
87
+ $nodes = [$nodes];
88
+
89
+ $returnArray = false;
90
+ }
91
+
92
+ $result = [];
93
+
94
+ Errors::disable();
95
+
96
+ foreach ($nodes as $node) {
97
+ if ($node instanceof Node) {
98
+ $node = $node->getNode();
99
+ }
100
+
101
+ if ( ! $node instanceof DOMNode) {
102
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($node) ? get_class($node) : gettype($node))));
103
+ }
104
+
105
+ $clonedNode = $node->cloneNode(true);
106
+ $newNode = $this->node->ownerDocument->importNode($clonedNode, true);
107
+
108
+ $result[] = $this->node->appendChild($newNode);
109
+ }
110
+
111
+ Errors::restore();
112
+
113
+ $result = array_map(function (DOMNode $node) {
114
+ return new Element($node);
115
+ }, $result);
116
+
117
+ return $returnArray ? $result : $result[0];
118
+ }
119
+
120
+ /**
121
+ * Adds a new child before a reference node.
122
+ *
123
+ * @param Node|DOMNode $node The new node
124
+ * @param Element|DOMNode|null $referenceNode The reference node
125
+ *
126
+ * @return Element
127
+ *
128
+ * @throws LogicException if the current node has no owner document
129
+ * @throws InvalidArgumentException if $node is not an instance of DOMNode or Element
130
+ * @throws InvalidArgumentException if $referenceNode is not an instance of DOMNode or Element
131
+ */
132
+ public function insertBefore($node, $referenceNode = null)
133
+ {
134
+ if ($this->node->ownerDocument === null) {
135
+ throw new LogicException('Can not insert a child to an element without the owner document');
136
+ }
137
+
138
+ if ($node instanceof Node) {
139
+ $node = $node->getNode();
140
+ }
141
+
142
+ if ( ! $node instanceof DOMNode) {
143
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($node) ? get_class($node) : gettype($node))));
144
+ }
145
+
146
+ if ($referenceNode !== null) {
147
+ if ($referenceNode instanceof Element) {
148
+ $referenceNode = $referenceNode->getNode();
149
+ }
150
+
151
+ if ( ! $referenceNode instanceof DOMNode) {
152
+ throw new InvalidArgumentException(sprintf('Argument 2 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($referenceNode) ? get_class($referenceNode) : gettype($referenceNode))));
153
+ }
154
+ }
155
+
156
+ Errors::disable();
157
+
158
+ $clonedNode = $node->cloneNode(true);
159
+ $newNode = $this->node->ownerDocument->importNode($clonedNode, true);
160
+
161
+ $insertedNode = $this->node->insertBefore($newNode, $referenceNode);
162
+
163
+ Errors::restore();
164
+
165
+ return new Element($insertedNode);
166
+ }
167
+
168
+ /**
169
+ * Adds a new child after a reference node.
170
+ *
171
+ * @param Node|DOMNode $node The new node
172
+ * @param Element|DOMNode|null $referenceNode The reference node
173
+ *
174
+ * @return Element
175
+ *
176
+ * @throws LogicException if the current node has no owner document
177
+ * @throws InvalidArgumentException if $node is not an instance of DOMNode or Element
178
+ * @throws InvalidArgumentException if $referenceNode is not an instance of DOMNode or Element
179
+ */
180
+ public function insertAfter($node, $referenceNode = null)
181
+ {
182
+ if ($referenceNode === null) {
183
+ return $this->insertBefore($node);
184
+ }
185
+
186
+ if ($referenceNode instanceof Node) {
187
+ $referenceNode = $referenceNode->getNode();
188
+ }
189
+
190
+ if ( ! $referenceNode instanceof DOMNode) {
191
+ throw new InvalidArgumentException(sprintf('Argument 2 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($referenceNode) ? get_class($referenceNode) : gettype($referenceNode))));
192
+ }
193
+
194
+ return $this->insertBefore($node, $referenceNode->nextSibling);
195
+ }
196
+
197
+ /**
198
+ * Adds a new sibling before a reference node.
199
+ *
200
+ * @param Node|DOMNode $node The new node
201
+ *
202
+ * @return Element
203
+ *
204
+ * @throws LogicException if the current node has no owner document
205
+ * @throws InvalidArgumentException if $node is not an instance of DOMNode or Element
206
+ * @throws InvalidArgumentException if $referenceNode is not an instance of DOMNode or Element
207
+ */
208
+ public function insertSiblingBefore($node)
209
+ {
210
+ if ($this->node->ownerDocument === null) {
211
+ throw new LogicException('Can not insert a child to an element without the owner document');
212
+ }
213
+
214
+ if ($this->parent() === null) {
215
+ throw new LogicException('Can not insert a child to an element without the parent element');
216
+ }
217
+
218
+ if ($node instanceof Node) {
219
+ $node = $node->getNode();
220
+ }
221
+
222
+ if ( ! $node instanceof DOMNode) {
223
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($node) ? get_class($node) : gettype($node))));
224
+ }
225
+
226
+ Errors::disable();
227
+
228
+ $clonedNode = $node->cloneNode(true);
229
+ $newNode = $this->node->ownerDocument->importNode($clonedNode, true);
230
+
231
+ $insertedNode = $this->parent()->getNode()->insertBefore($newNode, $this->node);
232
+
233
+ Errors::restore();
234
+
235
+ return new Element($insertedNode);
236
+ }
237
+
238
+ /**
239
+ * Adds a new sibling after a reference node.
240
+ *
241
+ * @param Node|DOMNode $node The new node
242
+ *
243
+ * @return Element
244
+ *
245
+ * @throws LogicException if the current node has no owner document
246
+ * @throws InvalidArgumentException if $node is not an instance of DOMNode or Element
247
+ * @throws InvalidArgumentException if $referenceNode is not an instance of DOMNode or Element
248
+ */
249
+ public function insertSiblingAfter($node)
250
+ {
251
+ if ($this->node->ownerDocument === null) {
252
+ throw new LogicException('Can not insert a child to an element without the owner document');
253
+ }
254
+
255
+ if ($this->parent() === null) {
256
+ throw new LogicException('Can not insert a child to an element without the parent element');
257
+ }
258
+
259
+ $nextSibling = $this->nextSibling();
260
+
261
+ // if the current node is the last child
262
+ if ($nextSibling === null) {
263
+ return $this->parent()->appendChild($node);
264
+ }
265
+
266
+ return $nextSibling->insertSiblingBefore($node);
267
+ }
268
+
269
+ /**
270
+ * Checks the existence of the node.
271
+ *
272
+ * @param string $expression XPath expression or CSS selector
273
+ * @param string $type The type of the expression
274
+ *
275
+ * @return bool
276
+ */
277
+ public function has($expression, $type = Query::TYPE_CSS)
278
+ {
279
+ return $this->toDocument()->has($expression, $type);
280
+ }
281
+
282
+ /**
283
+ * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
284
+ *
285
+ * @param string $expression XPath expression or a CSS selector
286
+ * @param string $type The type of the expression
287
+ * @param bool $wrapElement Returns array of Element if true, otherwise array of DOMElement
288
+ *
289
+ * @return Element[]|DOMElement[]
290
+ *
291
+ * @throws InvalidSelectorException
292
+ */
293
+ public function find($expression, $type = Query::TYPE_CSS, $wrapElement = true)
294
+ {
295
+ return $this->toDocument()->find($expression, $type, $wrapElement);
296
+ }
297
+
298
+ /**
299
+ * Searches for an node in the owner document using current node as context.
300
+ *
301
+ * @param string $expression XPath expression or a CSS selector
302
+ * @param string $type The type of the expression
303
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
304
+ *
305
+ * @return Element[]|DOMElement[]
306
+ *
307
+ * @throws LogicException if the current node has no owner document
308
+ * @throws InvalidSelectorException
309
+ */
310
+ public function findInDocument($expression, $type = Query::TYPE_CSS, $wrapNode = true)
311
+ {
312
+ $ownerDocument = $this->getDocument();
313
+
314
+ if ($ownerDocument === null) {
315
+ throw new LogicException('Can not search in context without the owner document');
316
+ }
317
+
318
+ return $ownerDocument->find($expression, $type, $wrapNode, $this->node);
319
+ }
320
+
321
+ /**
322
+ * Searches for an node in the DOM tree and returns first element or null.
323
+ *
324
+ * @param string $expression XPath expression or a CSS selector
325
+ * @param string $type The type of the expression
326
+ * @param bool $wrapNode Returns Element if true, otherwise DOMElement
327
+ *
328
+ * @return Element|DOMElement|null
329
+ *
330
+ * @throws InvalidSelectorException
331
+ */
332
+ public function first($expression, $type = Query::TYPE_CSS, $wrapNode = true)
333
+ {
334
+ return $this->toDocument()->first($expression, $type, $wrapNode);
335
+ }
336
+
337
+ /**
338
+ * Searches for an node in the owner document using current node as context and returns first element or null.
339
+ *
340
+ * @param string $expression XPath expression or a CSS selector
341
+ * @param string $type The type of the expression
342
+ * @param bool $wrapNode Returns Element if true, otherwise DOMElement
343
+ *
344
+ * @return Element|DOMElement|null
345
+ *
346
+ * @throws InvalidSelectorException
347
+ */
348
+ public function firstInDocument($expression, $type = Query::TYPE_CSS, $wrapNode = true)
349
+ {
350
+ $ownerDocument = $this->getDocument();
351
+
352
+ if ($ownerDocument === null) {
353
+ throw new LogicException('Can not search in context without the owner document');
354
+ }
355
+
356
+ return $ownerDocument->first($expression, $type, $wrapNode, $this->node);
357
+ }
358
+
359
+ /**
360
+ * Searches for an node in the DOM tree for a given XPath expression.
361
+ *
362
+ * @param string $expression XPath expression
363
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
364
+ *
365
+ * @return Element[]|DOMElement[]
366
+ *
367
+ * @throws InvalidSelectorException
368
+ */
369
+ public function xpath($expression, $wrapNode = true)
370
+ {
371
+ return $this->find($expression, Query::TYPE_XPATH, $wrapNode);
372
+ }
373
+
374
+ /**
375
+ * Counts nodes for a given XPath expression or a CSS selector.
376
+ *
377
+ * @param string $expression XPath expression or CSS selector
378
+ * @param string $type The type of the expression
379
+ *
380
+ * @return int
381
+ *
382
+ * @throws InvalidSelectorException
383
+ */
384
+ public function count($expression, $type = Query::TYPE_CSS)
385
+ {
386
+ return $this->toDocument()->count($expression, $type);
387
+ }
388
+
389
+ /**
390
+ * Dumps the node into a string using HTML formatting (including child nodes).
391
+ *
392
+ * @return string
393
+ */
394
+ public function html()
395
+ {
396
+ return $this->toDocument()->html();
397
+ }
398
+
399
+ /**
400
+ * Dumps the node into a string using HTML formatting (without child nodes).
401
+ *
402
+ * @return string
403
+ */
404
+ public function outerHtml()
405
+ {
406
+ $document = new DOMDocument();
407
+
408
+ $importedNode = $document->importNode($this->node);
409
+
410
+ return $document->saveHTML($importedNode);
411
+ }
412
+
413
+ /**
414
+ * Dumps the node descendants into a string using HTML formatting.
415
+ *
416
+ * @param string $delimiter
417
+ *
418
+ * @return string
419
+ */
420
+ public function innerHtml($delimiter = '')
421
+ {
422
+ $innerHtml = [];
423
+
424
+ foreach ($this->node->childNodes as $childNode) {
425
+ $innerHtml[] = $childNode->ownerDocument->saveHTML($childNode);
426
+ }
427
+
428
+ return implode($delimiter, $innerHtml);
429
+ }
430
+
431
+ /**
432
+ * Dumps the node descendants into a string using XML formatting.
433
+ *
434
+ * @param string $delimiter
435
+ *
436
+ * @return string
437
+ */
438
+ public function innerXml($delimiter = '')
439
+ {
440
+ $innerXml = [];
441
+
442
+ foreach ($this->node->childNodes as $childNode) {
443
+ $innerXml[] = $childNode->ownerDocument->saveXML($childNode);
444
+ }
445
+
446
+ return implode($delimiter, $innerXml);
447
+ }
448
+
449
+ /**
450
+ * Sets inner HTML.
451
+ *
452
+ * @param string $html
453
+ *
454
+ * @return static
455
+ *
456
+ * @throws InvalidArgumentException if passed argument is not a string
457
+ * @throws InvalidSelectorException
458
+ */
459
+ public function setInnerHtml($html)
460
+ {
461
+ if ( ! is_string($html)) {
462
+ throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($html) ? get_class($html) : gettype($html))));
463
+ }
464
+
465
+ $this->removeChildren();
466
+
467
+ if ($html !== '') {
468
+ Errors::disable();
469
+
470
+ $html = "<htmlfragment>$html</htmlfragment>";
471
+
472
+ $document = new Document($html);
473
+
474
+ $fragment = $document->first('htmlfragment')->getNode();
475
+
476
+ foreach ($fragment->childNodes as $node) {
477
+ $newNode = $this->node->ownerDocument->importNode($node, true);
478
+
479
+ $this->node->appendChild($newNode);
480
+ }
481
+
482
+ Errors::restore();
483
+ }
484
+
485
+ return $this;
486
+ }
487
+
488
+ /**
489
+ * Dumps the node into a string using XML formatting.
490
+ *
491
+ * @param int $options Additional options
492
+ *
493
+ * @return string The node XML
494
+ */
495
+ public function xml($options = 0)
496
+ {
497
+ return $this->toDocument()->xml($options);
498
+ }
499
+
500
+ /**
501
+ * Get the text content of this node and its descendants.
502
+ *
503
+ * @return string The node value
504
+ */
505
+ public function text()
506
+ {
507
+ return $this->node->textContent;
508
+ }
509
+
510
+ /**
511
+ * Set the value of this node.
512
+ *
513
+ * @param string $value The new value of the node
514
+ *
515
+ * @return static
516
+ *
517
+ * @throws InvalidArgumentException if parameter 1 is not a string
518
+ */
519
+ public function setValue($value)
520
+ {
521
+ if (is_numeric($value)) {
522
+ $value = (string) $value;
523
+ }
524
+
525
+ if ( ! is_string($value) && $value !== null) {
526
+ throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
527
+ }
528
+
529
+ $this->node->nodeValue = $value;
530
+
531
+ return $this;
532
+ }
533
+
534
+ /**
535
+ * Returns true if the current node is a DOMElement instance.
536
+ *
537
+ * @return bool
538
+ */
539
+ public function isElementNode()
540
+ {
541
+ return $this->node instanceof DOMElement;
542
+ }
543
+
544
+ /**
545
+ * Returns true if the current node is a a DOMText instance.
546
+ *
547
+ * @return bool
548
+ */
549
+ public function isTextNode()
550
+ {
551
+ return $this->node instanceof DOMText;
552
+ }
553
+
554
+ /**
555
+ * Returns true if the current node is a DOMComment instance.
556
+ *
557
+ * @return bool
558
+ */
559
+ public function isCommentNode()
560
+ {
561
+ return $this->node instanceof DOMComment;
562
+ }
563
+
564
+ /**
565
+ * Returns true if the current node is a DOMCdataSection instance.
566
+ *
567
+ * @return bool
568
+ */
569
+ public function isCdataSectionNode()
570
+ {
571
+ return $this->node instanceof DOMCdataSection;
572
+ }
573
+
574
+ /**
575
+ * Indicates if two nodes are the same node.
576
+ *
577
+ * @param Element|DOMNode $node
578
+ *
579
+ * @return bool
580
+ *
581
+ * @throws InvalidArgumentException if parameter 1 is not an instance of DOMNode
582
+ */
583
+ public function is($node)
584
+ {
585
+ if ($node instanceof Node) {
586
+ $node = $node->getNode();
587
+ }
588
+
589
+ if ( ! $node instanceof DOMNode) {
590
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($node) ? get_class($node) : gettype($node))));
591
+ }
592
+
593
+ return $this->node->isSameNode($node);
594
+ }
595
+
596
+ /**
597
+ * @return Element|Document|null
598
+ */
599
+ public function parent()
600
+ {
601
+ if ($this->node->parentNode === null) {
602
+ return null;
603
+ }
604
+
605
+ if ($this->node->parentNode instanceof DOMDocument) {
606
+ return new Document($this->node->parentNode);
607
+ }
608
+
609
+ return new Element($this->node->parentNode);
610
+ }
611
+
612
+ /**
613
+ * Returns first parent node matches passed selector.
614
+ *
615
+ * @param string $selector
616
+ * @param bool $strict
617
+ *
618
+ * @return Element|null
619
+ *
620
+ * @throws InvalidSelectorException if the selector is invalid
621
+ */
622
+ public function closest($selector, $strict = false)
623
+ {
624
+ $node = $this;
625
+
626
+ while (true) {
627
+ $parent = $node->parent();
628
+
629
+ if ($parent === null || $parent instanceof Document) {
630
+ return null;
631
+ }
632
+
633
+ if ($parent->matches($selector, $strict)) {
634
+ return $parent;
635
+ }
636
+
637
+ $node = $parent;
638
+ }
639
+
640
+ return null;
641
+ }
642
+
643
+ /**
644
+ * @param string|null $selector
645
+ * @param string|null $nodeType
646
+ *
647
+ * @return Element|null
648
+ *
649
+ * @throws InvalidArgumentException if parameter 2 is not a string
650
+ * @throws RuntimeException if the node type is invalid
651
+ * @throws LogicException if the selector used with non DOMElement node type
652
+ * @throws InvalidSelectorException if the selector is invalid
653
+ */
654
+ public function previousSibling($selector = null, $nodeType = null)
655
+ {
656
+ if ($this->node->previousSibling === null) {
657
+ return null;
658
+ }
659
+
660
+ if ($selector === null && $nodeType === null) {
661
+ return new Element($this->node->previousSibling);
662
+ }
663
+
664
+ if ($selector !== null && $nodeType === null) {
665
+ $nodeType = 'DOMElement';
666
+ }
667
+
668
+ if ( ! is_string($nodeType)) {
669
+ throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
670
+ }
671
+
672
+ $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
673
+
674
+ if ( ! in_array($nodeType, $allowedTypes, true)) {
675
+ throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
676
+ }
677
+
678
+ if ($selector !== null && $nodeType !== 'DOMElement') {
679
+ throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
680
+ }
681
+
682
+ $node = $this->node->previousSibling;
683
+
684
+ while ($node !== null) {
685
+ if (get_class($node) !== $nodeType) {
686
+ $node = $node->previousSibling;
687
+
688
+ continue;
689
+ }
690
+
691
+ $element = new Element($node);
692
+
693
+ if ($selector === null) {
694
+ return $element;
695
+ }
696
+
697
+ if ($element->matches($selector)) {
698
+ return $element;
699
+ }
700
+
701
+ $node = $node->previousSibling;
702
+ }
703
+
704
+ return new Element($this->node->previousSibling);
705
+ }
706
+
707
+ /**
708
+ * @param string|null $selector
709
+ * @param string|null $nodeType
710
+ *
711
+ * @return Element[]
712
+ *
713
+ * @throws InvalidArgumentException if parameter 2 is not a string
714
+ * @throws RuntimeException if the node type is invalid
715
+ * @throws LogicException if the selector used with non DOMElement node type
716
+ * @throws InvalidSelectorException if the selector is invalid
717
+ */
718
+ public function previousSiblings($selector = null, $nodeType = null)
719
+ {
720
+ if ($this->node->previousSibling === null) {
721
+ return [];
722
+ }
723
+
724
+ if ($selector !== null && $nodeType === null) {
725
+ $nodeType = 'DOMElement';
726
+ }
727
+
728
+ if ($nodeType !== null) {
729
+ if ( ! is_string($nodeType)) {
730
+ throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
731
+ }
732
+
733
+ $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
734
+
735
+ if ( ! in_array($nodeType, $allowedTypes, true)) {
736
+ throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
737
+ }
738
+ }
739
+
740
+ if ($selector !== null && $nodeType !== 'DOMElement') {
741
+ throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
742
+ }
743
+
744
+ $result = [];
745
+
746
+ $node = $this->node->previousSibling;
747
+
748
+ while ($node !== null) {
749
+ $element = new Element($node);
750
+
751
+ if ($nodeType === null) {
752
+ $result[] = $element;
753
+
754
+ $node = $node->previousSibling;
755
+
756
+ continue;
757
+ }
758
+
759
+ if (get_class($node) !== $nodeType) {
760
+ $node = $node->previousSibling;
761
+
762
+ continue;
763
+ }
764
+
765
+ if ($selector === null) {
766
+ $result[] = $element;
767
+
768
+ $node = $node->previousSibling;
769
+
770
+ continue;
771
+ }
772
+
773
+ if ($element->matches($selector)) {
774
+ $result[] = $element;
775
+ }
776
+
777
+ $node = $node->previousSibling;
778
+ }
779
+
780
+ return array_reverse($result);
781
+ }
782
+
783
+ /**
784
+ * @param string|null $selector
785
+ * @param string|null $nodeType
786
+ *
787
+ * @return Element|null
788
+ *
789
+ * @throws InvalidArgumentException if parameter 2 is not a string
790
+ * @throws RuntimeException if the node type is invalid
791
+ * @throws LogicException if the selector used with non DOMElement node type
792
+ * @throws InvalidSelectorException if the selector is invalid
793
+ */
794
+ public function nextSibling($selector = null, $nodeType = null)
795
+ {
796
+ if ($this->node->nextSibling === null) {
797
+ return null;
798
+ }
799
+
800
+ if ($selector === null && $nodeType === null) {
801
+ return new Element($this->node->nextSibling);
802
+ }
803
+
804
+ if ($selector !== null && $nodeType === null) {
805
+ $nodeType = 'DOMElement';
806
+ }
807
+
808
+ if ( ! is_string($nodeType)) {
809
+ throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
810
+ }
811
+
812
+ $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
813
+
814
+ if ( ! in_array($nodeType, $allowedTypes, true)) {
815
+ throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
816
+ }
817
+
818
+ if ($selector !== null && $nodeType !== 'DOMElement') {
819
+ throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
820
+ }
821
+
822
+ $node = $this->node->nextSibling;
823
+
824
+ while ($node !== null) {
825
+ if (get_class($node) !== $nodeType) {
826
+ $node = $node->nextSibling;
827
+
828
+ continue;
829
+ }
830
+
831
+ $element = new Element($node);
832
+
833
+ if ($selector === null) {
834
+ return $element;
835
+ }
836
+
837
+ if ($element->matches($selector)) {
838
+ return $element;
839
+ }
840
+
841
+ $node = $node->nextSibling;
842
+ }
843
+
844
+ return null;
845
+ }
846
+
847
+ /**
848
+ * @param string|null $selector
849
+ * @param string $nodeType
850
+ *
851
+ * @return Element[]
852
+ *
853
+ * @throws InvalidArgumentException if parameter 2 is not a string
854
+ * @throws RuntimeException if the node type is invalid
855
+ * @throws LogicException if the selector used with non DOMElement node type
856
+ * @throws InvalidSelectorException if the selector is invalid
857
+ */
858
+ public function nextSiblings($selector = null, $nodeType = null)
859
+ {
860
+ if ($this->node->nextSibling === null) {
861
+ return [];
862
+ }
863
+
864
+ if ($selector !== null && $nodeType === null) {
865
+ $nodeType = 'DOMElement';
866
+ }
867
+
868
+ if ($nodeType !== null) {
869
+ if ( ! is_string($nodeType)) {
870
+ throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($nodeType)));
871
+ }
872
+
873
+ $allowedTypes = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection'];
874
+
875
+ if ( ! in_array($nodeType, $allowedTypes, true)) {
876
+ throw new RuntimeException(sprintf('Unknown node type "%s". Allowed types: %s', $nodeType, implode(', ', $allowedTypes)));
877
+ }
878
+ }
879
+
880
+ if ($selector !== null && $nodeType !== 'DOMElement') {
881
+ throw new LogicException(sprintf('Selector can be used only with DOMElement node type, %s given', $nodeType));
882
+ }
883
+
884
+ $result = [];
885
+
886
+ $node = $this->node->nextSibling;
887
+
888
+ while ($node !== null) {
889
+ $element = new Element($node);
890
+
891
+ if ($nodeType === null) {
892
+ $result[] = $element;
893
+
894
+ $node = $node->nextSibling;
895
+
896
+ continue;
897
+ }
898
+
899
+ if (get_class($node) !== $nodeType) {
900
+ $node = $node->nextSibling;
901
+
902
+ continue;
903
+ }
904
+
905
+ if ($selector === null) {
906
+ $result[] = $element;
907
+
908
+ $node = $node->nextSibling;
909
+
910
+ continue;
911
+ }
912
+
913
+ if ($element->matches($selector)) {
914
+ $result[] = $element;
915
+ }
916
+
917
+ $node = $node->nextSibling;
918
+ }
919
+
920
+ return $result;
921
+ }
922
+
923
+ /**
924
+ * @param int $index
925
+ *
926
+ * @return Element|null
927
+ */
928
+ public function child($index)
929
+ {
930
+ $child = $this->node->childNodes->item($index);
931
+
932
+ return $child === null ? null : new Element($child);
933
+ }
934
+
935
+ /**
936
+ * @return Element|null
937
+ */
938
+ public function firstChild()
939
+ {
940
+ if ($this->node->firstChild === null) {
941
+ return null;
942
+ }
943
+
944
+ return new Element($this->node->firstChild);
945
+ }
946
+
947
+ /**
948
+ * @return Element|null
949
+ */
950
+ public function lastChild()
951
+ {
952
+ if ($this->node->lastChild === null) {
953
+ return null;
954
+ }
955
+
956
+ return new Element($this->node->lastChild);
957
+ }
958
+
959
+ /**
960
+ * @return bool
961
+ */
962
+ public function hasChildren()
963
+ {
964
+ return $this->node->hasChildNodes();
965
+ }
966
+
967
+ /**
968
+ * @return Element[]
969
+ */
970
+ public function children()
971
+ {
972
+ $children = [];
973
+
974
+ foreach ($this->node->childNodes as $node) {
975
+ $children[] = new Element($node);
976
+ }
977
+
978
+ return $children;
979
+ }
980
+
981
+ /**
982
+ * Removes child from list of children.
983
+ *
984
+ * @param Node|DOMNode $childNode
985
+ *
986
+ * @return Element the node that has been removed
987
+ */
988
+ public function removeChild($childNode)
989
+ {
990
+ if ($childNode instanceof Node) {
991
+ $childNode = $childNode->getNode();
992
+ }
993
+
994
+ if ( ! $childNode instanceof DOMNode) {
995
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($childNode) ? get_class($childNode) : gettype($childNode))));
996
+ }
997
+
998
+ $removedNode = $this->node->removeChild($childNode);
999
+
1000
+ return new Element($removedNode);
1001
+ }
1002
+
1003
+ /**
1004
+ * Removes all child nodes.
1005
+ *
1006
+ * @return Element[] the nodes that has been removed
1007
+ */
1008
+ public function removeChildren()
1009
+ {
1010
+ // we need to collect child nodes to array
1011
+ // because removing nodes from the DOMNodeList on iterating is not working
1012
+ $childNodes = [];
1013
+
1014
+ foreach ($this->node->childNodes as $childNode) {
1015
+ $childNodes[] = $childNode;
1016
+ }
1017
+
1018
+ $removedNodes = [];
1019
+
1020
+ foreach ($childNodes as $childNode) {
1021
+ $removedNode = $this->node->removeChild($childNode);
1022
+
1023
+ $removedNodes[] = new Element($removedNode);
1024
+ }
1025
+
1026
+ return $removedNodes;
1027
+ }
1028
+
1029
+ /**
1030
+ * Removes current node from the parent.
1031
+ *
1032
+ * @return Element the node that has been removed
1033
+ *
1034
+ * @throws LogicException if the current node has no parent node
1035
+ */
1036
+ public function remove()
1037
+ {
1038
+ if ($this->node->parentNode === null) {
1039
+ throw new LogicException('Can not remove an element without the parent node');
1040
+ }
1041
+
1042
+ $removedNode = $this->node->parentNode->removeChild($this->node);
1043
+
1044
+ return new Element($removedNode);
1045
+ }
1046
+
1047
+ /**
1048
+ * Replaces a child.
1049
+ *
1050
+ * @param Node|DOMNode $newNode The new node
1051
+ * @param bool $clone Clone the node if true, otherwise move it
1052
+ *
1053
+ * @return Element The node that has been replaced
1054
+ *
1055
+ * @throws LogicException if the current node has no parent node
1056
+ */
1057
+ public function replace($newNode, $clone = true)
1058
+ {
1059
+ if ($this->node->parentNode === null) {
1060
+ throw new LogicException('Can not replace an element without the parent node');
1061
+ }
1062
+
1063
+ if ($newNode instanceof Node) {
1064
+ $newNode = $newNode->getNode();
1065
+ }
1066
+
1067
+ if ( ! $newNode instanceof DOMNode) {
1068
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of %s or DOMNode, %s given', __METHOD__, __CLASS__, (is_object($newNode) ? get_class($newNode) : gettype($newNode))));
1069
+ }
1070
+
1071
+ if ($clone) {
1072
+ $newNode = $newNode->cloneNode(true);
1073
+ }
1074
+
1075
+ if ($newNode->ownerDocument === null || ! $this->getDocument()->is($newNode->ownerDocument)) {
1076
+ $newNode = $this->node->ownerDocument->importNode($newNode, true);
1077
+ }
1078
+
1079
+ $node = $this->node->parentNode->replaceChild($newNode, $this->node);
1080
+
1081
+ return new Element($node);
1082
+ }
1083
+
1084
+ /**
1085
+ * Get line number for a node.
1086
+ *
1087
+ * @return int
1088
+ */
1089
+ public function getLineNo()
1090
+ {
1091
+ return $this->node->getLineNo();
1092
+ }
1093
+
1094
+ /**
1095
+ * Clones a node.
1096
+ *
1097
+ * @param bool $deep Indicates whether to copy all descendant nodes
1098
+ *
1099
+ * @return Element The cloned node
1100
+ */
1101
+ public function cloneNode($deep = true)
1102
+ {
1103
+ return new Element($this->node->cloneNode($deep));
1104
+ }
1105
+
1106
+ /**
1107
+ * Sets current node instance.
1108
+ *
1109
+ * @param DOMElement|DOMText|DOMComment|DOMCdataSection|DOMDocumentFragment $node
1110
+ *
1111
+ * @return static
1112
+ */
1113
+ protected function setNode($node)
1114
+ {
1115
+ $allowedClasses = ['DOMElement', 'DOMText', 'DOMComment', 'DOMCdataSection', 'DOMDocumentFragment'];
1116
+
1117
+ if ( ! is_object($node) || ! in_array(get_class($node), $allowedClasses, true)) {
1118
+ throw new InvalidArgumentException(sprintf('Argument 1 passed to %s must be an instance of DOMElement, DOMText, DOMComment, DOMCdataSection or DOMDocumentFragment, %s given', __METHOD__, (is_object($node) ? get_class($node) : gettype($node))));
1119
+ }
1120
+
1121
+ $this->node = $node;
1122
+
1123
+ return $this;
1124
+ }
1125
+
1126
+ /**
1127
+ * Returns current node instance.
1128
+ *
1129
+ * @return DOMElement|DOMText|DOMComment|DOMCdataSection|DOMDocumentFragment
1130
+ */
1131
+ public function getNode()
1132
+ {
1133
+ return $this->node;
1134
+ }
1135
+
1136
+ /**
1137
+ * Returns the document associated with this node.
1138
+ *
1139
+ * @return Document|null
1140
+ */
1141
+ public function getDocument()
1142
+ {
1143
+ if ($this->node->ownerDocument === null) {
1144
+ return null;
1145
+ }
1146
+
1147
+ return new Document($this->node->ownerDocument);
1148
+ }
1149
+
1150
+ /**
1151
+ * Get the DOM document with the current element.
1152
+ *
1153
+ * @param string $encoding The document encoding
1154
+ *
1155
+ * @return Document
1156
+ */
1157
+ public function toDocument($encoding = 'UTF-8')
1158
+ {
1159
+ $document = new Document(null, false, $encoding);
1160
+
1161
+ $document->appendChild($this->node);
1162
+
1163
+ return $document;
1164
+ }
1165
+
1166
+ /**
1167
+ * Convert the element to its string representation.
1168
+ *
1169
+ * @return string
1170
+ */
1171
+ public function __toString()
1172
+ {
1173
+ return $this->html();
1174
+ }
1175
+
1176
+ /**
1177
+ * Searches for an node in the DOM tree for a given XPath expression or a CSS selector.
1178
+ *
1179
+ * @param string $expression XPath expression or a CSS selector
1180
+ * @param string $type The type of the expression
1181
+ * @param bool $wrapNode Returns array of Element if true, otherwise array of DOMElement
1182
+ *
1183
+ * @return Element[]|DOMElement[]
1184
+ *
1185
+ * @throws InvalidSelectorException
1186
+ *
1187
+ * @deprecated Not longer recommended, use Element::find() instead.
1188
+ */
1189
+ public function __invoke($expression, $type = Query::TYPE_CSS, $wrapNode = true)
1190
+ {
1191
+ return $this->find($expression, $type, $wrapNode);
1192
+ }
1193
+ }
vendor/imangazaliev/didom/src/DiDom/Query.php CHANGED
@@ -33,11 +33,11 @@ class Query
33
  */
34
  public static function compile($expression, $type = self::TYPE_CSS)
35
  {
36
- if (!is_string($expression)) {
37
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($expression)));
38
  }
39
 
40
- if (!is_string($type)) {
41
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($type)));
42
  }
43
 
@@ -55,7 +55,7 @@ class Query
55
  return $expression;
56
  }
57
 
58
- if (!array_key_exists($expression, static::$compiled)) {
59
  static::$compiled[$expression] = static::cssToXpath($expression);
60
  }
61
 
@@ -69,6 +69,8 @@ class Query
69
  * @param string $prefix Specifies the nesting of nodes
70
  *
71
  * @return string XPath expression
 
 
72
  */
73
  public static function cssToXpath($selector, $prefix = '//')
74
  {
@@ -92,6 +94,8 @@ class Query
92
  * @param string $prefix
93
  *
94
  * @return array
 
 
95
  */
96
  protected static function parseAndConvertSelector($selector, $prefix = '//')
97
  {
@@ -162,26 +166,26 @@ class Query
162
 
163
  /**
164
  * @param string $name
165
- * @param array $args
166
  *
167
  * @return string
168
  *
169
- * @throws InvalidSelectorException if the passed property is unknown
170
  */
171
- protected static function convertProperty($name, array $args = [])
172
  {
173
  if ($name === 'text') {
174
  return 'text()';
175
  }
176
 
177
  if ($name === 'attr') {
178
- if (count($args) === 0) {
179
  return '@*';
180
  }
181
 
182
  $attributes = [];
183
 
184
- foreach ($args as $attribute) {
185
  $attributes[] = sprintf('name() = "%s"', $attribute);
186
  }
187
 
@@ -200,7 +204,7 @@ class Query
200
  *
201
  * @return string
202
  *
203
- * @throws InvalidSelectorException if passed an unknown pseudo-class
204
  */
205
  protected static function convertPseudo($pseudo, &$tagName, array $parameters = [])
206
  {
@@ -305,7 +309,7 @@ class Query
305
  $attributes[] = self::convertPseudo($segments['pseudo'], $tagName, $parameters);
306
  }
307
 
308
- if (count($attributes) === 0 && !isset($segments['tag'])) {
309
  throw new InvalidArgumentException('The array of segments must contain the name of the tag or at least one attribute');
310
  }
311
 
@@ -319,15 +323,15 @@ class Query
319
  }
320
 
321
  /**
322
- * @param string $name The attribute name
323
- * @param string $value The attribute value
324
  *
325
  * @return string
326
  */
327
  protected static function convertAttribute($name, $value)
328
  {
329
- $isSimpleSelector = !in_array(substr($name, 0, 1), ['^', '!'], true);
330
- $isSimpleSelector = $isSimpleSelector && (!in_array(substr($name, -1), ['^', '$', '*', '!', '~'], true));
331
 
332
  if ($isSimpleSelector) {
333
  // if specified only the attribute name
@@ -383,8 +387,7 @@ class Query
383
  *
384
  * @return string
385
  *
386
- * @throws InvalidSelectorException if passed nth-child is empty
387
- * @throws InvalidSelectorException if passed an unknown nth-child expression
388
  */
389
  protected static function convertNthExpression($expression)
390
  {
@@ -419,8 +422,8 @@ class Query
419
 
420
  /**
421
  * @param string $string
422
- * @param bool $caseSensitive
423
- * @param bool $fullMatch
424
  *
425
  * @return string
426
  */
@@ -430,17 +433,17 @@ class Query
430
  return sprintf('text() = "%s"', $string);
431
  }
432
 
433
- if ($caseSensitive && !$fullMatch) {
434
  return sprintf('contains(text(), "%s")', $string);
435
  }
436
 
437
  $strToLowerFunction = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
438
 
439
- if (!$caseSensitive && $fullMatch) {
440
  return sprintf("php:functionString(\"{$strToLowerFunction}\", .) = php:functionString(\"{$strToLowerFunction}\", \"%s\")", $string);
441
  }
442
 
443
- // if !$caseSensitive and !$fullMatch
444
  return sprintf("contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"%s\"))", $string);
445
  }
446
 
@@ -550,7 +553,7 @@ class Query
550
  /**
551
  * @param array $compiled
552
  *
553
- * @throws \InvalidArgumentException if the attributes is not an array
554
  */
555
  public static function setCompiled(array $compiled)
556
  {
33
  */
34
  public static function compile($expression, $type = self::TYPE_CSS)
35
  {
36
+ if ( ! is_string($expression)) {
37
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, gettype($expression)));
38
  }
39
 
40
+ if ( ! is_string($type)) {
41
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, gettype($type)));
42
  }
43
 
55
  return $expression;
56
  }
57
 
58
+ if ( ! array_key_exists($expression, static::$compiled)) {
59
  static::$compiled[$expression] = static::cssToXpath($expression);
60
  }
61
 
69
  * @param string $prefix Specifies the nesting of nodes
70
  *
71
  * @return string XPath expression
72
+ *
73
+ * @throws InvalidSelectorException
74
  */
75
  public static function cssToXpath($selector, $prefix = '//')
76
  {
94
  * @param string $prefix
95
  *
96
  * @return array
97
+ *
98
+ * @throws InvalidSelectorException
99
  */
100
  protected static function parseAndConvertSelector($selector, $prefix = '//')
101
  {
166
 
167
  /**
168
  * @param string $name
169
+ * @param array $parameters
170
  *
171
  * @return string
172
  *
173
+ * @throws InvalidSelectorException if the specified property is unknown
174
  */
175
+ protected static function convertProperty($name, array $parameters = [])
176
  {
177
  if ($name === 'text') {
178
  return 'text()';
179
  }
180
 
181
  if ($name === 'attr') {
182
+ if (count($parameters) === 0) {
183
  return '@*';
184
  }
185
 
186
  $attributes = [];
187
 
188
+ foreach ($parameters as $attribute) {
189
  $attributes[] = sprintf('name() = "%s"', $attribute);
190
  }
191
 
204
  *
205
  * @return string
206
  *
207
+ * @throws InvalidSelectorException if the specified pseudo-class is unknown
208
  */
209
  protected static function convertPseudo($pseudo, &$tagName, array $parameters = [])
210
  {
309
  $attributes[] = self::convertPseudo($segments['pseudo'], $tagName, $parameters);
310
  }
311
 
312
+ if (count($attributes) === 0 && ! isset($segments['tag'])) {
313
  throw new InvalidArgumentException('The array of segments must contain the name of the tag or at least one attribute');
314
  }
315
 
323
  }
324
 
325
  /**
326
+ * @param string $name The name of an attribute
327
+ * @param string $value The value of an attribute
328
  *
329
  * @return string
330
  */
331
  protected static function convertAttribute($name, $value)
332
  {
333
+ $isSimpleSelector = ! in_array(substr($name, 0, 1), ['^', '!'], true);
334
+ $isSimpleSelector = $isSimpleSelector && ( ! in_array(substr($name, -1), ['^', '$', '*', '!', '~'], true));
335
 
336
  if ($isSimpleSelector) {
337
  // if specified only the attribute name
387
  *
388
  * @return string
389
  *
390
+ * @throws InvalidSelectorException if the given nth-child expression is empty or invalid
 
391
  */
392
  protected static function convertNthExpression($expression)
393
  {
422
 
423
  /**
424
  * @param string $string
425
+ * @param bool $caseSensitive
426
+ * @param bool $fullMatch
427
  *
428
  * @return string
429
  */
433
  return sprintf('text() = "%s"', $string);
434
  }
435
 
436
+ if ($caseSensitive && ! $fullMatch) {
437
  return sprintf('contains(text(), "%s")', $string);
438
  }
439
 
440
  $strToLowerFunction = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
441
 
442
+ if ( ! $caseSensitive && $fullMatch) {
443
  return sprintf("php:functionString(\"{$strToLowerFunction}\", .) = php:functionString(\"{$strToLowerFunction}\", \"%s\")", $string);
444
  }
445
 
446
+ // if ! $caseSensitive and ! $fullMatch
447
  return sprintf("contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"%s\"))", $string);
448
  }
449
 
553
  /**
554
  * @param array $compiled
555
  *
556
+ * @throws InvalidArgumentException if the attributes is not an array
557
  */
558
  public static function setCompiled(array $compiled)
559
  {
vendor/imangazaliev/didom/src/DiDom/StyleAttribute.php CHANGED
@@ -9,7 +9,7 @@ class StyleAttribute
9
  /**
10
  * The DOM element instance.
11
  *
12
- * @var \DiDom\Element
13
  */
14
  protected $element;
15
 
@@ -24,15 +24,13 @@ class StyleAttribute
24
  protected $properties = [];
25
 
26
  /**
27
- * Constructor.
28
  *
29
- * @param \DiDom\Element $element
30
- *
31
- * @throws \InvalidArgumentException if passed argument is not an element node
32
  */
33
  public function __construct(Element $element)
34
  {
35
- if (!$element->isElementNode()) {
36
  throw new InvalidArgumentException(sprintf('The element must contain DOMElement node'));
37
  }
38
 
@@ -46,7 +44,7 @@ class StyleAttribute
46
  */
47
  protected function parseStyleAttribute()
48
  {
49
- if (!$this->element->hasAttribute('style')) {
50
  // possible if style attribute has been removed
51
  if ($this->styleString !== '') {
52
  $this->styleString = '';
@@ -112,18 +110,18 @@ class StyleAttribute
112
  * @param string $name
113
  * @param string $value
114
  *
115
- * @return \DiDom\StyleAttribute
116
  *
117
- * @throws \InvalidArgumentException if property name is not a string
118
- * @throws \InvalidArgumentException if property value is not a string
119
  */
120
  public function setProperty($name, $value)
121
  {
122
- if (!is_string($name)) {
123
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
124
  }
125
 
126
- if (!is_string($value)) {
127
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
128
  }
129
 
@@ -139,21 +137,21 @@ class StyleAttribute
139
  /**
140
  * @param array $properties
141
  *
142
- * @return \DiDom\StyleAttribute
143
  *
144
- * @throws \InvalidArgumentException if property name is not a string
145
- * @throws \InvalidArgumentException if property value is not a string
146
  */
147
  public function setMultipleProperties(array $properties)
148
  {
149
  $this->parseStyleAttribute();
150
 
151
  foreach ($properties as $propertyName => $value) {
152
- if (!is_string($propertyName)) {
153
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
154
  }
155
 
156
- if (!is_string($value)) {
157
  throw new InvalidArgumentException(sprintf('Property value must be a string, %s given', (is_object($value) ? get_class($value) : gettype($value))));
158
  }
159
 
@@ -173,13 +171,13 @@ class StyleAttribute
173
  */
174
  public function getProperty($name, $default = null)
175
  {
176
- if (!is_string($name)) {
177
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
178
  }
179
 
180
  $this->parseStyleAttribute();
181
 
182
- if (!array_key_exists($name, $this->properties)) {
183
  return $default;
184
  }
185
 
@@ -191,7 +189,7 @@ class StyleAttribute
191
  *
192
  * @return mixed
193
  *
194
- * @throws \InvalidArgumentException if property name is not a string
195
  */
196
  public function getMultipleProperties(array $propertyNames)
197
  {
@@ -200,7 +198,7 @@ class StyleAttribute
200
  $result = [];
201
 
202
  foreach ($propertyNames as $propertyName) {
203
- if (!is_string($propertyName)) {
204
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
205
  }
206
 
@@ -229,7 +227,7 @@ class StyleAttribute
229
  */
230
  public function hasProperty($name)
231
  {
232
- if (!is_string($name)) {
233
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
234
  }
235
 
@@ -241,13 +239,13 @@ class StyleAttribute
241
  /**
242
  * @param string $name
243
  *
244
- * @return \DiDom\StyleAttribute
245
  *
246
- * @throws \InvalidArgumentException if property name is not a string
247
  */
248
  public function removeProperty($name)
249
  {
250
- if (!is_string($name)) {
251
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
252
  }
253
 
@@ -263,16 +261,16 @@ class StyleAttribute
263
  /**
264
  * @param array $propertyNames
265
  *
266
- * @return \DiDom\StyleAttribute
267
  *
268
- * @throws \InvalidArgumentException if property name is not a string
269
  */
270
  public function removeMultipleProperties(array $propertyNames)
271
  {
272
  $this->parseStyleAttribute();
273
 
274
  foreach ($propertyNames as $propertyName) {
275
- if (!is_string($propertyName)) {
276
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
277
  }
278
 
@@ -287,7 +285,7 @@ class StyleAttribute
287
  /**
288
  * @param string[] $exclusions
289
  *
290
- * @return \DiDom\StyleAttribute
291
  */
292
  public function removeAllProperties(array $exclusions = [])
293
  {
@@ -296,11 +294,11 @@ class StyleAttribute
296
  $preservedProperties = [];
297
 
298
  foreach ($exclusions as $propertyName) {
299
- if (!is_string($propertyName)) {
300
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
301
  }
302
 
303
- if (!array_key_exists($propertyName, $this->properties)) {
304
  continue;
305
  }
306
 
@@ -315,7 +313,7 @@ class StyleAttribute
315
  }
316
 
317
  /**
318
- * @return \DiDom\Element
319
  */
320
  public function getElement()
321
  {
9
  /**
10
  * The DOM element instance.
11
  *
12
+ * @var Element
13
  */
14
  protected $element;
15
 
24
  protected $properties = [];
25
 
26
  /**
27
+ * @param Element $element
28
  *
29
+ * @throws InvalidArgumentException if parameter 1 is not an element node
 
 
30
  */
31
  public function __construct(Element $element)
32
  {
33
+ if ( ! $element->isElementNode()) {
34
  throw new InvalidArgumentException(sprintf('The element must contain DOMElement node'));
35
  }
36
 
44
  */
45
  protected function parseStyleAttribute()
46
  {
47
+ if ( ! $this->element->hasAttribute('style')) {
48
  // possible if style attribute has been removed
49
  if ($this->styleString !== '') {
50
  $this->styleString = '';
110
  * @param string $name
111
  * @param string $value
112
  *
113
+ * @return StyleAttribute
114
  *
115
+ * @throws InvalidArgumentException if property name is not a string
116
+ * @throws InvalidArgumentException if property value is not a string
117
  */
118
  public function setProperty($name, $value)
119
  {
120
+ if ( ! is_string($name)) {
121
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
122
  }
123
 
124
+ if ( ! is_string($value)) {
125
  throw new InvalidArgumentException(sprintf('%s expects parameter 2 to be string, %s given', __METHOD__, (is_object($value) ? get_class($value) : gettype($value))));
126
  }
127
 
137
  /**
138
  * @param array $properties
139
  *
140
+ * @return StyleAttribute
141
  *
142
+ * @throws InvalidArgumentException if property name is not a string
143
+ * @throws InvalidArgumentException if property value is not a string
144
  */
145
  public function setMultipleProperties(array $properties)
146
  {
147
  $this->parseStyleAttribute();
148
 
149
  foreach ($properties as $propertyName => $value) {
150
+ if ( ! is_string($propertyName)) {
151
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
152
  }
153
 
154
+ if ( ! is_string($value)) {
155
  throw new InvalidArgumentException(sprintf('Property value must be a string, %s given', (is_object($value) ? get_class($value) : gettype($value))));
156
  }
157
 
171
  */
172
  public function getProperty($name, $default = null)
173
  {
174
+ if ( ! is_string($name)) {
175
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
176
  }
177
 
178
  $this->parseStyleAttribute();
179
 
180
+ if ( ! array_key_exists($name, $this->properties)) {
181
  return $default;
182
  }
183
 
189
  *
190
  * @return mixed
191
  *
192
+ * @throws InvalidArgumentException if property name is not a string
193
  */
194
  public function getMultipleProperties(array $propertyNames)
195
  {
198
  $result = [];
199
 
200
  foreach ($propertyNames as $propertyName) {
201
+ if ( ! is_string($propertyName)) {
202
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
203
  }
204
 
227
  */
228
  public function hasProperty($name)
229
  {
230
+ if ( ! is_string($name)) {
231
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
232
  }
233
 
239
  /**
240
  * @param string $name
241
  *
242
+ * @return StyleAttribute
243
  *
244
+ * @throws InvalidArgumentException if property name is not a string
245
  */
246
  public function removeProperty($name)
247
  {
248
+ if ( ! is_string($name)) {
249
  throw new InvalidArgumentException(sprintf('%s expects parameter 1 to be string, %s given', __METHOD__, (is_object($name) ? get_class($name) : gettype($name))));
250
  }
251
 
261
  /**
262
  * @param array $propertyNames
263
  *
264
+ * @return StyleAttribute
265
  *
266
+ * @throws InvalidArgumentException if property name is not a string
267
  */
268
  public function removeMultipleProperties(array $propertyNames)
269
  {
270
  $this->parseStyleAttribute();
271
 
272
  foreach ($propertyNames as $propertyName) {
273
+ if ( ! is_string($propertyName)) {
274
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
275
  }
276
 
285
  /**
286
  * @param string[] $exclusions
287
  *
288
+ * @return StyleAttribute
289
  */
290
  public function removeAllProperties(array $exclusions = [])
291
  {
294
  $preservedProperties = [];
295
 
296
  foreach ($exclusions as $propertyName) {
297
+ if ( ! is_string($propertyName)) {
298
  throw new InvalidArgumentException(sprintf('Property name must be a string, %s given', (is_object($propertyName) ? get_class($propertyName) : gettype($propertyName))));
299
  }
300
 
301
+ if ( ! array_key_exists($propertyName, $this->properties)) {
302
  continue;
303
  }
304
 
313
  }
314
 
315
  /**
316
+ * @return Element
317
  */
318
  public function getElement()
319
  {
vendor/imangazaliev/didom/tests/DiDom/ClassAttributeTest.php DELETED
@@ -1,383 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Element;
6
- use DiDom\ClassAttribute;
7
- use Tests\TestCase;
8
-
9
- class ClassAttributeTest extends TestCase
10
- {
11
- /**
12
- * @expectedException \InvalidArgumentException
13
- * @expectedExceptionMessage The element must contain DOMElement node
14
- */
15
- public function testConstructorWithTextNode()
16
- {
17
- $element = new Element(new \DOMText('foo'));
18
-
19
- new ClassAttribute($element);
20
- }
21
-
22
- /**
23
- * @expectedException \InvalidArgumentException
24
- * @expectedExceptionMessage The element must contain DOMElement node
25
- */
26
- public function testConstructorWithCommentNode()
27
- {
28
- $element = new Element(new \DOMComment('foo'));
29
-
30
- new ClassAttribute($element);
31
- }
32
-
33
- /**
34
- * @expectedException \InvalidArgumentException
35
- * @expectedExceptionMessage DiDom\ClassAttribute::add expects parameter 1 to be string, NULL given
36
- */
37
- public function testAddWithInvalidClassName()
38
- {
39
- $element = new Element('div', null, [
40
- 'class' => 'foo',
41
- ]);
42
-
43
- $classAttribute = new ClassAttribute($element);
44
-
45
- $classAttribute->add(null);
46
- }
47
-
48
- public function testAdd()
49
- {
50
- // without class attribute
51
- $element = new Element('div', null);
52
-
53
- $classAttribute = new ClassAttribute($element);
54
-
55
- $this->assertEquals(null, $element->getAttribute('class'));
56
-
57
- $classAttribute->add('foo');
58
-
59
- $this->assertEquals('foo', $element->getAttribute('class'));
60
-
61
- // with empty class attribute
62
- $element = new Element('div', null, [
63
- 'class' => '',
64
- ]);
65
-
66
- $classAttribute = new ClassAttribute($element);
67
-
68
- $this->assertEquals('', $element->getAttribute('class'));
69
-
70
- $classAttribute->add('foo');
71
-
72
- $this->assertEquals('foo', $element->getAttribute('class'));
73
-
74
- // class attribute with spaces
75
- $element = new Element('div', null, [
76
- 'class' => ' ',
77
- ]);
78
-
79
- $classAttribute = new ClassAttribute($element);
80
-
81
- $this->assertEquals(' ', $element->getAttribute('class'));
82
-
83
- $classAttribute->add('foo');
84
-
85
- $this->assertEquals('foo', $element->getAttribute('class'));
86
-
87
- // with not empty class attribute
88
- $element = new Element('div', null, [
89
- 'class' => 'foo bar',
90
- ]);
91
-
92
- $classAttribute = new ClassAttribute($element);
93
-
94
- $this->assertEquals('foo bar', $element->getAttribute('class'));
95
-
96
- $classAttribute->add('baz');
97
-
98
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
99
-
100
- // with existing class name
101
- $element = new Element('div', null, [
102
- 'class' => 'foo bar baz',
103
- ]);
104
-
105
- $classAttribute = new ClassAttribute($element);
106
-
107
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
108
-
109
- $classAttribute->add('bar');
110
-
111
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
112
- }
113
-
114
- /**
115
- * @expectedException \InvalidArgumentException
116
- * @expectedExceptionMessage Class name must be a string, NULL given
117
- */
118
- public function testAddMultipleWithInvalidClassName()
119
- {
120
- $element = new Element('div', null, [
121
- 'class' => 'foo',
122
- ]);
123
-
124
- $classAttribute = new ClassAttribute($element);
125
-
126
- $classAttribute->addMultiple(['bar', null]);
127
- }
128
-
129
- public function testAddMultiple()
130
- {
131
- $element = new Element('div', null, [
132
- 'class' => 'foo',
133
- ]);
134
-
135
- $classAttribute = new ClassAttribute($element);
136
-
137
- $this->assertEquals('foo', $element->getAttribute('class'));
138
-
139
- $classAttribute->addMultiple([
140
- 'bar', 'baz',
141
- ]);
142
-
143
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
144
- }
145
-
146
- public function testGetAll()
147
- {
148
- // without class attribute
149
- $element = new Element('div', null);
150
-
151
- $classAttribute = new ClassAttribute($element);
152
-
153
- $this->assertEquals([], $classAttribute->getAll());
154
-
155
- // with empty class attribute
156
- $element = new Element('div', null, [
157
- 'class' => '',
158
- ]);
159
-
160
- $classAttribute = new ClassAttribute($element);
161
-
162
- $this->assertEquals([], $classAttribute->getAll());
163
-
164
- // class attribute with spaces
165
- $element = new Element('div', null, [
166
- 'class' => ' ',
167
- ]);
168
-
169
- $classAttribute = new ClassAttribute($element);
170
-
171
- $this->assertEquals([], $classAttribute->getAll());
172
-
173
- // one class
174
- $element = new Element('div', null, [
175
- 'class' => 'foo',
176
- ]);
177
-
178
- $classAttribute = new ClassAttribute($element);
179
-
180
- $this->assertEquals(['foo'], $classAttribute->getAll());
181
-
182
- // several classes
183
- $element = new Element('div', null, [
184
- 'class' => 'foo bar baz',
185
- ]);
186
-
187
- $classAttribute = new ClassAttribute($element);
188
-
189
- $this->assertEquals(['foo', 'bar', 'baz'], $classAttribute->getAll());
190
-
191
- // with multiple spaces between class names
192
- $element = new Element('div', null, [
193
- 'class' => 'foo bar baz',
194
- ]);
195
-
196
- $classAttribute = new ClassAttribute($element);
197
-
198
- $this->assertEquals(['foo', 'bar', 'baz'], $classAttribute->getAll());
199
- }
200
-
201
- public function testGetAllPropertiesAfterEmptyClassAttribute()
202
- {
203
- $element = new Element('div', null, [
204
- 'class' => 'foo bar baz',
205
- ]);
206
-
207
- $classAttribute = new ClassAttribute($element);
208
-
209
- $this->assertEquals(['foo', 'bar', 'baz'], $classAttribute->getAll());
210
-
211
- $element->setAttribute('class', '');
212
-
213
- $this->assertEquals([], $classAttribute->getAll());
214
- }
215
-
216
- public function testContains()
217
- {
218
- $element = new Element('div', null, [
219
- 'class' => 'foo bar',
220
- ]);
221
-
222
- $classAttribute = new ClassAttribute($element);
223
-
224
- $this->assertTrue($classAttribute->contains('foo'));
225
- $this->assertTrue($classAttribute->contains('bar'));
226
- $this->assertFalse($classAttribute->contains('baz'));
227
- }
228
-
229
- /**
230
- * @expectedException \InvalidArgumentException
231
- * @expectedExceptionMessage DiDom\ClassAttribute::remove expects parameter 1 to be string, NULL given
232
- */
233
- public function testRemoveWithInvalidClassName()
234
- {
235
- $element = new Element('div', null, [
236
- 'class' => 'foo bar baz',
237
- ]);
238
-
239
- $classAttribute = new ClassAttribute($element);
240
-
241
- $classAttribute->remove(null);
242
- }
243
-
244
- public function testRemove()
245
- {
246
- $element = new Element('div', null, [
247
- 'class' => 'foo bar baz',
248
- ]);
249
-
250
- $classAttribute = new ClassAttribute($element);
251
-
252
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
253
-
254
- $classAttribute->remove('bar');
255
-
256
- $this->assertEquals('foo baz', $element->getAttribute('class'));
257
-
258
- // with nonexistent class name
259
- $element = new Element('div', null, [
260
- 'class' => 'foo bar',
261
- ]);
262
-
263
- $classAttribute = new ClassAttribute($element);
264
-
265
- $this->assertEquals('foo bar', $element->getAttribute('class'));
266
-
267
- $classAttribute->remove('baz');
268
-
269
- $this->assertEquals('foo bar', $element->getAttribute('class'));
270
- }
271
-
272
- /**
273
- * @expectedException \InvalidArgumentException
274
- * @expectedExceptionMessage Class name must be a string, NULL given
275
- */
276
- public function testRemoveMultipleWithInvalidClassName()
277
- {
278
- $element = new Element('div', null, [
279
- 'class' => 'foo bar baz',
280
- ]);
281
-
282
- $classAttribute = new ClassAttribute($element);
283
-
284
- $classAttribute->removeMultiple(['foo', null]);
285
- }
286
-
287
- public function testRemoveMultiple()
288
- {
289
- $element = new Element('div', null, [
290
- 'class' => 'foo bar baz',
291
- ]);
292
-
293
- $classAttribute = new ClassAttribute($element);
294
-
295
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
296
-
297
- $classAttribute->removeMultiple(['foo', 'bar']);
298
-
299
- $this->assertEquals('baz', $element->getAttribute('class'));
300
-
301
- // with nonexistent class name
302
- $element = new Element('div', null, [
303
- 'class' => 'foo bar baz',
304
- ]);
305
-
306
- $classAttribute = new ClassAttribute($element);
307
-
308
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
309
-
310
- $classAttribute->removeMultiple(['bar', 'qux']);
311
-
312
- $this->assertEquals('foo baz', $element->getAttribute('class'));
313
- }
314
-
315
- /**
316
- * @expectedException \InvalidArgumentException
317
- * @expectedExceptionMessage Class name must be a string, NULL given
318
- */
319
- public function testRemoveAllWithInvalidClassName()
320
- {
321
- $element = new Element('div', null, [
322
- 'class' => 'foo bar baz',
323
- ]);
324
-
325
- $classAttribute = new ClassAttribute($element);
326
-
327
- $classAttribute->removeAll(['foo', null]);
328
- }
329
-
330
- public function testRemoveAll()
331
- {
332
- $element = new Element('div', null, [
333
- 'class' => 'foo bar baz',
334
- ]);
335
-
336
- $classAttribute = new ClassAttribute($element);
337
-
338
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
339
-
340
- $classAttribute->removeAll();
341
-
342
- $this->assertEquals('', $element->getAttribute('class'));
343
- }
344
-
345
- public function testRemoveAllWithExclusions()
346
- {
347
- $element = new Element('div', null, [
348
- 'class' => 'foo bar baz',
349
- ]);
350
-
351
- $classAttribute = new ClassAttribute($element);
352
-
353
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
354
-
355
- $classAttribute->removeAll(['bar']);
356
-
357
- $this->assertEquals('bar', $element->getAttribute('class'));
358
-
359
- // with nonexistent class name
360
- $element = new Element('div', null, [
361
- 'class' => 'foo bar baz',
362
- ]);
363
-
364
- $classAttribute = new ClassAttribute($element);
365
-
366
- $this->assertEquals('foo bar baz', $element->getAttribute('class'));
367
-
368
- $classAttribute->removeAll(['bar', 'qux']);
369
-
370
- $this->assertEquals('bar', $element->getAttribute('class'));
371
- }
372
-
373
- public function testGetElement()
374
- {
375
- $element = new Element('div', null, [
376
- 'class' => 'foo bar baz',
377
- ]);
378
-
379
- $classAttribute = new ClassAttribute($element);
380
-
381
- $this->assertSame($element, $classAttribute->getElement());
382
- }
383
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/DiDom/DocumentTest.php DELETED
@@ -1,772 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Document;
6
- use DiDom\Query;
7
- use Tests\TestCase;
8
-
9
- class DocumentTest extends TestCase
10
- {
11
- /**
12
- * @expectedException \InvalidArgumentException
13
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, array given
14
- */
15
- public function testConstructWithInvalidArgument()
16
- {
17
- new Document(array('foo'));
18
- }
19
-
20
- /**
21
- * @expectedException \InvalidArgumentException
22
- * @expectedExceptionMessage DiDom\Document::__construct expects parameter 3 to be string, NULL given
23
- */
24
- public function testConstructWithInvalidEncoding()
25
- {
26
- new Document(null, false, null);
27
- }
28
-
29
- /**
30
- * @expectedException \RuntimeException
31
- * @expectedExceptionMessage Could not load file path/to/file
32
- */
33
- public function testConstructWithNotExistingFile()
34
- {
35
- new Document('path/to/file', true);
36
- }
37
-
38
- /**
39
- * @expectedException \InvalidArgumentException
40
- * @expectedExceptionMessage DiDom\Document::load expects parameter 3 to be string, NULL given
41
- */
42
- public function testConstructorWithInvalidTypeOfDocumentTypeArgument()
43
- {
44
- new Document('foo', false, 'UTF-8', null);
45
- }
46
-
47
- /**
48
- * @expectedException \RuntimeException
49
- * @expectedExceptionMessage Document type must be "xml" or "html", bar given
50
- */
51
- public function testConstructorWithInvalidDocumentType()
52
- {
53
- new Document('foo', false, 'UTF-8', 'bar');
54
- }
55
-
56
- /**
57
- * @dataProvider loadHtmlCharsetTests
58
- */
59
- public function testLoadHtmlCharset($html, $text)
60
- {
61
- $document = new Document($html, false, 'UTF-8');
62
-
63
- $this->assertEquals($text, $document->first('div')->text());
64
- }
65
-
66
- public function loadHtmlCharsetTests()
67
- {
68
- return array(
69
- array('<html><div class="foo">English language</html>', 'English language'),
70
- array('<html><div class="foo">Русский язык</html>', 'Русский язык'),
71
- array('<html><div class="foo">اللغة العربية</html>', 'اللغة العربية'),
72
- array('<html><div class="foo">漢語</html>', '漢語'),
73
- array('<html><div class="foo">Tiếng Việt</html>', 'Tiếng Việt'),
74
- );
75
- }
76
-
77
- public function testCreate()
78
- {
79
- $this->assertInstanceOf('DiDom\Document', Document::create());
80
- }
81
-
82
- public function testCreateElement()
83
- {
84
- $html = $this->loadFixture('posts.html');
85
-
86
- $document = new Document($html, false);
87
- $element = $document->createElement('span', 'value');
88
-
89
- $this->assertInstanceOf('DiDom\Element', $element);
90
- $this->assertEquals('span', $element->getNode()->tagName);
91
- $this->assertEquals('value', $element->getNode()->textContent);
92
-
93
- $element = $document->createElement('span');
94
- $this->assertEquals('', $element->text());
95
-
96
- $element = $document->createElement('input', '', ['name' => 'username']);
97
- $this->assertEquals('username', $element->getNode()->getAttribute('name'));
98
- }
99
-
100
- public function testCreateElementBySelector()
101
- {
102
- $document = new Document();
103
-
104
- $element = $document->createElementBySelector('a.external-link[href=http://example.com]');
105
-
106
- $this->assertEquals('a', $element->tag);
107
- $this->assertEquals('', $element->text());
108
- $this->assertEquals(['href' => 'http://example.com', 'class' => 'external-link'], $element->attributes());
109
-
110
- $element = $document->createElementBySelector('#block', 'Foo');
111
-
112
- $this->assertEquals('div', $element->tag);
113
- $this->assertEquals('Foo', $element->text());
114
- $this->assertEquals(['id' => 'block'], $element->attributes());
115
-
116
- $element = $document->createElementBySelector('input', null, ['name' => 'name', 'placeholder' => 'Enter your name']);
117
-
118
- $this->assertEquals('input', $element->tag);
119
- $this->assertEquals('', $element->text());
120
- $this->assertEquals(['name' => 'name', 'placeholder' => 'Enter your name'], $element->attributes());
121
- }
122
-
123
- public function testCreateTextNode()
124
- {
125
- $document = new Document();
126
-
127
- $textNode = $document->createTextNode('foo bar baz');
128
-
129
- $this->assertInstanceOf('DiDom\Element', $textNode);
130
- $this->assertInstanceOf('DOMText', $textNode->getNode());
131
- $this->assertEquals('foo bar baz', $textNode->text());
132
- }
133
-
134
- public function testCreateComment()
135
- {
136
- $document = new Document();
137
-
138
- $comment = $document->createComment('foo bar baz');
139
-
140
- $this->assertInstanceOf('DiDom\Element', $comment);
141
- $this->assertInstanceOf('DOMComment', $comment->getNode());
142
- $this->assertEquals('foo bar baz', $comment->text());
143
- }
144
-
145
- public function testCreateCDATASection()
146
- {
147
- $document = new Document();
148
-
149
- $cdataSection = $document->createCdataSection('foo bar baz');
150
-
151
- $this->assertInstanceOf('DiDom\Element', $cdataSection);
152
- $this->assertInstanceOf('DOMCdataSection', $cdataSection->getNode());
153
- $this->assertEquals('foo bar baz', $cdataSection->text());
154
- }
155
-
156
- /**
157
- * @expectedException \InvalidArgumentException
158
- * @expectedExceptionMessage Argument 1 passed to DiDom\Document::appendChild must be an instance of DiDom\Element or DOMNode, string given
159
- */
160
- public function testAppendChildWithInvalidArgument()
161
- {
162
- $html = $this->loadFixture('posts.html');
163
-
164
- $document = new Document($html);
165
-
166
- $document->appendChild('foo');
167
- }
168
-
169
- public function testAppendChild()
170
- {
171
- $html = '<!DOCTYPE html>
172
- <html lang="en">
173
- <head>
174
- <meta charset="UTF-8">
175
- <title>Document</title>
176
- </head>
177
- <body>
178
-
179
- </body>
180
- </html>';
181
-
182
- $document = new Document($html);
183
-
184
- $this->assertCount(0, $document->find('span'));
185
-
186
- $node = $document->createElement('span');
187
- $appendedChild = $document->appendChild($node);
188
-
189
- $this->assertCount(1, $document->find('span'));
190
- $this->assertTrue($appendedChild->is($document->first('span')));
191
-
192
- $appendedChild->remove();
193
-
194
- $this->assertCount(0, $document->find('span'));
195
-
196
- $nodes = [];
197
- $nodes[] = $document->createElement('span');
198
- $nodes[] = $document->createElement('span');
199
-
200
- $appendedChildren = $document->appendChild($nodes);
201
-
202
- $nodes = $document->find('span');
203
-
204
- $this->assertCount(2, $appendedChildren);
205
- $this->assertCount(2, $nodes);
206
-
207
- foreach ($appendedChildren as $index => $child) {
208
- $this->assertTrue($child->is($nodes[$index]));
209
- }
210
- }
211
-
212
- /**
213
- * @expectedException \InvalidArgumentException
214
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, NULL given
215
- */
216
- public function testLoadWithInvalidContentArgument()
217
- {
218
- $document = new Document();
219
-
220
- $document->load(null);
221
- }
222
-
223
- /**
224
- * @expectedException \RuntimeException
225
- * @expectedExceptionMessage Could not load file path/to/file
226
- */
227
- public function testLoadWithNotExistingFile()
228
- {
229
- $document = new Document();
230
-
231
- $document->load('path/to/file', true);
232
- }
233
-
234
- /**
235
- * @expectedException \InvalidArgumentException
236
- * @expectedExceptionMessage DiDom\Document::load expects parameter 3 to be string, NULL given
237
- */
238
- public function testLoadWithInvalidTypeOfDocumentTypeArgument()
239
- {
240
- $document = new Document();
241
-
242
- $document->load('foo', false, null);
243
- }
244
-
245
- /**
246
- * @expectedException \RuntimeException
247
- * @expectedExceptionMessage Document type must be "xml" or "html", bar given
248
- */
249
- public function testLoadWithInvalidDocumentType()
250
- {
251
- $document = new Document();
252
-
253
- $document->load('foo', false, 'bar');
254
- }
255
-
256
- /**
257
- * @expectedException \InvalidArgumentException
258
- * @expectedExceptionMessage DiDom\Document::load expects parameter 4 to be integer, string given
259
- */
260
- public function testLoadWithInvalidOptionsType()
261
- {
262
- $document = new Document();
263
-
264
- $document->load('foo', false, 'html', 'bar');
265
- }
266
-
267
- public function testLoadHtmlDocument()
268
- {
269
- $html = '
270
- <!DOCTYPE html>
271
- <html>
272
- <head>
273
- <title>Document</title>
274
- </head>
275
- <body>
276
- <div class="foo">Foo — Bar — Baz</div>
277
- </body>
278
- </html>
279
- ';
280
-
281
- $document = new Document();
282
-
283
- $document->load($html, false, 'html');
284
-
285
- $this->assertEquals('Foo — Bar — Baz', $document->first('.foo')->text());
286
- }
287
-
288
- public function testLoadXmlDocument()
289
- {
290
- $xml = '
291
- <?xml version="1.0" encoding="UTF-8"?>
292
- <root>
293
- <foo>Foo — Bar — Baz</foo>
294
- </root>
295
- ';
296
-
297
- $document = new Document();
298
-
299
- $document->load($xml, false, 'xml');
300
-
301
- $this->assertEquals('Foo — Bar — Baz', $document->first('foo')->text());
302
- }
303
-
304
- /**
305
- * @expectedException \InvalidArgumentException
306
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, NULL given
307
- */
308
- public function testLoadHtmlWithInvalidArgument()
309
- {
310
- $document = new Document();
311
-
312
- $document->loadHtml(null);
313
- }
314
-
315
- /**
316
- * @expectedException \InvalidArgumentException
317
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, array given
318
- */
319
- public function testLoadHtmlFileWithInvalidArgument()
320
- {
321
- $document = new Document();
322
-
323
- $document->loadHtmlFile(array('foo'));
324
- }
325
-
326
- /**
327
- * @expectedException \RuntimeException
328
- * @expectedExceptionMessage Could not load file path/to/file
329
- */
330
- public function testLoadHtmlFileWithNotExistingFile()
331
- {
332
- $document = new Document();
333
-
334
- $document->loadHtmlFile('path/to/file');
335
- }
336
-
337
- /**
338
- * @expectedException \InvalidArgumentException
339
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, NULL given
340
- */
341
- public function testLoadXmlWithInvalidArgument()
342
- {
343
- $document = new Document();
344
-
345
- $document->loadXml(null);
346
- }
347
-
348
- /**
349
- * @expectedException \InvalidArgumentException
350
- * @expectedExceptionMessage DiDom\Document::load expects parameter 1 to be string, array given
351
- */
352
- public function testLoadXmlFileWithInvalidArgument()
353
- {
354
- $document = new Document();
355
-
356
- $document->loadXmlFile(array('foo'));
357
- }
358
-
359
- /**
360
- * @expectedException \RuntimeException
361
- * @expectedExceptionMessage Could not load file path/to/file
362
- */
363
- public function testLoadXmlFileWithNotExistingFile()
364
- {
365
- $document = new Document();
366
-
367
- $document->loadXmlFile('path/to/file');
368
- }
369
-
370
- public function testHas()
371
- {
372
- $document = new Document($this->loadFixture('posts.html'));
373
-
374
- $this->assertTrue($document->has('.posts'));
375
- $this->assertFalse($document->has('.fake'));
376
- }
377
-
378
- /**
379
- * @dataProvider findTests
380
- */
381
- public function testFind($html, $selector, $type, $count)
382
- {
383
- $document = new Document($html);
384
- $elements = $document->find($selector, $type);
385
-
386
- $this->assertTrue(is_array($elements));
387
- $this->assertEquals($count, count($elements));
388
-
389
- foreach ($elements as $element) {
390
- $this->assertInstanceOf('DiDom\Element', $element);
391
- }
392
- }
393
-
394
- /**
395
- * @dataProvider findTests
396
- */
397
- public function testFindAndReturnDomElement($html, $selector, $type, $count)
398
- {
399
- $document = new Document($html);
400
- $elements = $document->find($selector, $type, false);
401
-
402
- $this->assertTrue(is_array($elements));
403
- $this->assertEquals($count, count($elements));
404
-
405
- foreach ($elements as $element) {
406
- $this->assertInstanceOf('DOMElement', $element);
407
- }
408
- }
409
-
410
- public function testFindWithContext()
411
- {
412
- $document = new Document($this->loadFixture('posts.html'));
413
-
414
- $post = $document->find('.post')[1];
415
- $title = $document->find('.post .title')[1];
416
-
417
- $titleInContext = $document->find('.title', Query::TYPE_CSS, true, $post)[0];
418
-
419
- $this->assertTrue($title->is($titleInContext));
420
- $this->assertFalse($title->is($post->find('.title')[0]));
421
- }
422
-
423
- public function testFindText()
424
- {
425
- $html = $this->loadFixture('menu.html');
426
-
427
- $document = new Document($html);
428
- $texts = $document->find('//a/text()', Query::TYPE_XPATH);
429
-
430
- $this->assertTrue(is_array($texts));
431
- $this->assertEquals(3, count($texts));
432
-
433
- $this->assertEquals(['Link 1', 'Link 2', 'Link 3'], $texts);
434
- }
435
-
436
- public function testFindComment()
437
- {
438
- $html = $this->loadFixture('menu.html');
439
-
440
- $document = new Document($html);
441
-
442
- $comment = $document->xpath('/html/body/ul/li/a/comment()');
443
- $this->assertTrue($comment[0]->isCommentNode());
444
- $this->assertTrue($comment[1]->isCommentNode());
445
-
446
- $this->assertTrue(is_array($comment));
447
- $this->assertEquals(2, count($comment));
448
-
449
- $comment = $document->xpath('/html/body/comment()');
450
- $this->assertTrue($comment[0]->isCommentNode());
451
-
452
- $this->assertTrue(is_array($comment));
453
- $this->assertEquals(1, count($comment));
454
- }
455
-
456
- public function testFindAttribute()
457
- {
458
- $html = $this->loadFixture('menu.html');
459
-
460
- $document = new Document($html);
461
- $links = $document->find('//a/@href', Query::TYPE_XPATH);
462
-
463
- $this->assertTrue(is_array($links));
464
- $this->assertEquals(3, count($links));
465
-
466
- foreach ($links as $link) {
467
- $this->assertEquals('http://example.com', $link);
468
- }
469
- }
470
-
471
- public function findTests()
472
- {
473
- $html = $this->loadFixture('posts.html');
474
-
475
- return array(
476
- array($html, '.post h2', Query::TYPE_CSS, 3),
477
- array($html, '.fake h2', Query::TYPE_CSS, 0),
478
- array($html, '.post h2, .post p', Query::TYPE_CSS, 6),
479
- array($html, "//*[contains(concat(' ', normalize-space(@class), ' '), ' post ')]", Query::TYPE_XPATH, 3),
480
- );
481
- }
482
-
483
- public function testFirst()
484
- {
485
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
486
-
487
- $document = new Document($html, false);
488
-
489
- $items = $document->find('ul > li');
490
-
491
- $this->assertEquals($items[0]->getNode(), $document->first('ul > li')->getNode());
492
-
493
- $this->assertEquals('One', $document->first('ul > li::text'));
494
-
495
- $document = new Document();
496
-
497
- $this->assertNull($document->first('ul > li'));
498
- }
499
-
500
- public function testFirstWithContext()
501
- {
502
- $html = '
503
- <div class="root">
504
- <span>Foo</span>
505
-
506
- <div><span>Bar</span></div>
507
- </div>
508
- ';
509
-
510
- $document = new Document($html);
511
-
512
- $div = $document->first('.root div');
513
- $span = $document->first('.root div span');
514
-
515
- $result = $document->first('span', Query::TYPE_CSS, true, $div);
516
-
517
- $this->assertTrue($span->is($result));
518
- }
519
-
520
- public function testXpath()
521
- {
522
- $html = $this->loadFixture('posts.html');
523
-
524
- $document = new Document($html, false);
525
- $elements = $document->xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' post ')]");
526
-
527
- $this->assertTrue(is_array($elements));
528
- $this->assertEquals(3, count($elements));
529
-
530
- foreach ($elements as $element) {
531
- $this->assertInstanceOf('DiDom\Element', $element);
532
- }
533
- }
534
-
535
- public function testCount()
536
- {
537
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
538
-
539
- $document = new Document($html, false);
540
-
541
- $this->assertInternalType('int', $document->count('li'));
542
- $this->assertEquals(3, $document->count('li'));
543
-
544
- $document = new Document();
545
-
546
- $this->assertInternalType('int', $document->count('li'));
547
- $this->assertEquals(0, $document->count('li'));
548
- }
549
-
550
- public function testCreateXpath()
551
- {
552
- $document = new Document();
553
-
554
- $xpath =$document->createXpath();
555
-
556
- $this->assertInstanceOf('DOMXPath', $xpath);
557
- $this->assertEquals($document->getDocument(), $xpath->document);
558
- }
559
-
560
- public function testHtml()
561
- {
562
- $html = '
563
- <!DOCTYPE html>
564
- <html lang="en">
565
- <head>
566
- <meta charset="UTF-8">
567
- <title>Document</title>
568
- </head>
569
- <body>
570
- English language <br>
571
- Русский язык <br>
572
- اللغة العربية <br>
573
- 漢語 <br>
574
- Tiếng Việt <br>
575
-
576
- &lt; &gt;
577
- </body>
578
- </html>
579
- ';
580
-
581
- $document = new Document($html);
582
-
583
- $this->assertEquals(trim($html), $document->html());
584
- }
585
-
586
- public function testXml()
587
- {
588
- $xml = $this->loadFixture('books.xml');
589
- $document = new Document($xml, false, 'UTF-8', 'xml');
590
-
591
- $this->assertTrue(is_string($document->xml()));
592
- }
593
-
594
- public function testXmlWithOptions()
595
- {
596
- $xml = '<foo><bar></bar></foo>';
597
-
598
- $document = new Document();
599
- $document->loadXml($xml);
600
-
601
- $prolog = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
602
-
603
- $this->assertEquals($prolog.'<foo><bar/></foo>', $document->xml());
604
- $this->assertEquals($prolog.'<foo><bar></bar></foo>', $document->xml(LIBXML_NOEMPTYTAG));
605
- }
606
-
607
- public function testFormat()
608
- {
609
- $html = $this->loadFixture('posts.html');
610
- $document = new Document($html, false);
611
-
612
- $this->assertFalse($document->getDocument()->formatOutput);
613
-
614
- $document->format();
615
-
616
- $this->assertTrue($document->getDocument()->formatOutput);
617
- }
618
-
619
- public function testText()
620
- {
621
- $html = '<html>foo</html>';
622
- $document = new Document($html, false);
623
-
624
- $this->assertEquals('foo', $document->text());
625
- }
626
-
627
- /**
628
- * @expectedException InvalidArgumentException
629
- */
630
- public function testIsWithInvalidArgument()
631
- {
632
- $document = new Document();
633
- $document->is(null);
634
- }
635
-
636
- public function testIs()
637
- {
638
- $html = $this->loadFixture('posts.html');
639
-
640
- $document = new Document($html, false);
641
- $document2 = new Document($html, false);
642
-
643
- $this->assertTrue($document->is($document));
644
- $this->assertFalse($document->is($document2));
645
- }
646
-
647
- public function testIsWithEmptyDocument()
648
- {
649
- $html = $this->loadFixture('posts.html');
650
-
651
- $document = new Document($html, false);
652
- $document2 = new Document();
653
-
654
- $this->assertFalse($document->is($document2));
655
- }
656
-
657
- public function testGetType()
658
- {
659
- // empty document
660
-
661
- $document = new Document();
662
-
663
- $this->assertNull($document->getType());
664
-
665
- // html
666
-
667
- $html = $this->loadFixture('posts.html');
668
-
669
- $document = new Document($html);
670
- $this->assertEquals('html', $document->getType());
671
-
672
- $document = new Document();
673
- $document->loadHtml($html);
674
- $this->assertEquals('html', $document->getType());
675
-
676
- $document = new Document();
677
- $document->load($html, false, 'html');
678
- $this->assertEquals('html', $document->getType());
679
-
680
- // xml
681
-
682
- $xml = $this->loadFixture('books.xml');
683
-
684
- $document = new Document($xml, false, 'UTF-8', 'xml');
685
- $this->assertEquals('xml', $document->getType());
686
-
687
- $document = new Document();
688
- $document->loadXml($xml);
689
- $this->assertEquals('xml', $document->getType());
690
-
691
- $document = new Document();
692
- $document->load($xml, false, 'xml');
693
- $this->assertEquals('xml', $document->getType());
694
- }
695
-
696
- public function testGetEncoding()
697
- {
698
- $document = new Document();
699
-
700
- $this->assertEquals('UTF-8', $document->getEncoding());
701
-
702
- $document = new Document(null, false, 'CP-1251');
703
-
704
- $this->assertEquals('CP-1251', $document->getEncoding());
705
- }
706
-
707
- public function testGetDocument()
708
- {
709
- $domDocument = new \DOMDocument();
710
- $document = new Document($domDocument);
711
-
712
- $this->assertEquals($domDocument, $document->getDocument());
713
- }
714
-
715
- public function testGetElement()
716
- {
717
- $html = $this->loadFixture('posts.html');
718
- $document = new Document($html, false);
719
-
720
- $this->assertInstanceOf('DOMElement', $document->getElement());
721
- }
722
-
723
- /**
724
- * @expectedException RuntimeException
725
- */
726
- public function testEmptyDocumentToElement()
727
- {
728
- $document = new Document();
729
-
730
- $document->toElement();
731
- }
732
-
733
- public function testToElement()
734
- {
735
- $html = $this->loadFixture('posts.html');
736
- $document = new Document($html, false);
737
-
738
- $this->assertInstanceOf('DiDom\Element', $document->toElement());
739
- }
740
-
741
- public function testToStringHtml()
742
- {
743
- $html = $this->loadFixture('posts.html');
744
- $document = new Document($html, false);
745
-
746
- $this->assertEquals($document->html(), $document->__toString());
747
- }
748
-
749
- public function testToStringXml()
750
- {
751
- $xml = $this->loadFixture('books.xml');
752
- $document = new Document($xml, false, 'UTF-8', 'xml');
753
-
754
- $this->assertEquals($document->xml(), $document->__toString());
755
- }
756
-
757
- /**
758
- * @dataProvider findTests
759
- */
760
- public function testInvoke($html, $selector, $type, $count)
761
- {
762
- $document = new Document($html, false);
763
- $elements = $document($selector, $type);
764
-
765
- $this->assertTrue(is_array($elements));
766
- $this->assertEquals($count, count($elements));
767
-
768
- foreach ($elements as $element) {
769
- $this->assertInstanceOf('DiDom\Element', $element);
770
- }
771
- }
772
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/DiDom/ElementTest.php DELETED
@@ -1,2094 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Document;
6
- use DiDom\Element;
7
- use DiDom\Query;
8
- use Tests\TestCase;
9
-
10
- class ElementTest extends TestCase
11
- {
12
- /**
13
- * @expectedException \InvalidArgumentException
14
- */
15
- public function testConstructorWithNullTagName()
16
- {
17
- new Element(null);
18
- }
19
-
20
- /**
21
- * @expectedException \InvalidArgumentException
22
- */
23
- public function testConstructorWithInvalidTagNameType()
24
- {
25
- new Element([]);
26
- }
27
-
28
- /**
29
- * @expectedException \InvalidArgumentException
30
- */
31
- public function testConstructorWithInvalidObject()
32
- {
33
- new Element(new \StdClass());
34
- }
35
-
36
- /**
37
- * @expectedException \InvalidArgumentException
38
- */
39
- public function testConstructorWithInvalidValue()
40
- {
41
- new Element('span', []);
42
- }
43
-
44
- public function testConstructorWithInvalidAttributes()
45
- {
46
- if (PHP_VERSION_ID >= 70000) {
47
- $this->setExpectedException('TypeError');
48
- } else {
49
- $this->setExpectedException('PHPUnit_Framework_Error');
50
- }
51
-
52
- new Element('span', 'Foo', null);
53
- }
54
-
55
- public function testConstructor()
56
- {
57
- $element = new Element('input', null, ['name' => 'username', 'value' => 'John']);
58
-
59
- $this->assertEquals('input', $element->getNode()->tagName);
60
- $this->assertEquals('username', $element->getNode()->getAttribute('name'));
61
- $this->assertEquals('John', $element->getNode()->getAttribute('value'));
62
-
63
- // create from DOMElement
64
- $node = new \DOMElement('span', 'Foo');
65
- $element = new Element($node);
66
-
67
- $this->assertEquals($node, $element->getNode());
68
-
69
- // create from DOMText
70
- $node = new \DOMText('Foo');
71
- $element = new Element($node);
72
-
73
- $this->assertEquals($node, $element->getNode());
74
-
75
- // create from DOMComment
76
- $node = new \DOMComment('Foo');
77
- $element = new Element($node);
78
-
79
- $this->assertEquals($node, $element->getNode());
80
- }
81
-
82
- public function testCreate()
83
- {
84
- $element = Element::create('span', 'Foo', ['class' => 'bar']);
85
-
86
- $this->assertEquals('span', $element->tag);
87
- $this->assertEquals('Foo', $element->text());
88
- $this->assertEquals(['class' => 'bar'], $element->attributes());
89
- }
90
-
91
- public function testCreateBySelector()
92
- {
93
- $element = Element::createBySelector('li.item.active', 'Foo', ['data-id' => 1]);
94
-
95
- $this->assertEquals('li', $element->tag);
96
- $this->assertEquals('Foo', $element->text());
97
- $this->assertEquals(['class' => 'item active', 'data-id' => 1], $element->attributes());
98
- }
99
-
100
- /**
101
- * @expectedException \InvalidArgumentException
102
- */
103
- public function testPrependChildWithInvalidArgument()
104
- {
105
- $element = new Element('span', 'hello');
106
-
107
- $element->prependChild('foo');
108
- }
109
-
110
- /**
111
- * @expectedException \LogicException
112
- * @expectedExceptionMessage Can not prepend child to element without owner document
113
- */
114
- public function testPrependChildWithoutParentNode()
115
- {
116
- $element = new Element(new \DOMElement('div'));
117
-
118
- $element->prependChild(new Element('div'));
119
- }
120
-
121
- public function testPrependChild()
122
- {
123
- $list = new Element('ul');
124
-
125
- $this->assertEquals(0, $list->getNode()->childNodes->length);
126
-
127
- $item = new Element('li', 'bar');
128
-
129
- $prependedChild = $list->prependChild($item);
130
-
131
- $this->assertEquals(1, $list->getNode()->childNodes->length);
132
- $this->assertInstanceOf('DiDom\Element', $prependedChild);
133
- $this->assertEquals('bar', $prependedChild->getNode()->textContent);
134
-
135
- $item = new Element('li', 'foo');
136
-
137
- $prependedChild = $list->prependChild($item);
138
-
139
- $this->assertEquals(2, $list->getNode()->childNodes->length);
140
- $this->assertInstanceOf('DiDom\Element', $prependedChild);
141
- $this->assertEquals('foo', $prependedChild->getNode()->textContent);
142
-
143
- $this->assertEquals('foo', $list->getNode()->childNodes->item(0)->textContent);
144
- $this->assertEquals('bar', $list->getNode()->childNodes->item(1)->textContent);
145
- }
146
-
147
- public function testPrependChildWithArrayOfNodes()
148
- {
149
- $list = new Element('ul');
150
-
151
- $prependedChild = $list->prependChild(new Element('li', 'foo'));
152
-
153
- $this->assertEquals(1, $list->getNode()->childNodes->length);
154
- $this->assertInstanceOf('DiDom\Element', $prependedChild);
155
- $this->assertEquals('foo', $prependedChild->getNode()->textContent);
156
-
157
- $items = [];
158
-
159
- $items[] = new Element('li', 'bar');
160
- $items[] = new Element('li', 'baz');
161
-
162
- $appendedChildren = $list->prependChild($items);
163
-
164
- $this->assertCount(2, $appendedChildren);
165
- $this->assertEquals(3, $list->getNode()->childNodes->length);
166
-
167
- foreach ($appendedChildren as $appendedChild) {
168
- $this->assertInstanceOf('DiDom\Element', $appendedChild);
169
- }
170
-
171
- foreach (['bar', 'baz', 'foo'] as $index => $value) {
172
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
173
- }
174
- }
175
-
176
- /**
177
- * @expectedException \InvalidArgumentException
178
- */
179
- public function testAppendChildWithInvalidArgument()
180
- {
181
- $element = new Element('span', 'hello');
182
-
183
- $element->appendChild('foo');
184
- }
185
-
186
- /**
187
- * @expectedException \LogicException
188
- * @expectedExceptionMessage Can not append child to element without owner document
189
- */
190
- public function testAppendChildWithoutParentNode()
191
- {
192
- $element = new Element(new \DOMElement('div'));
193
-
194
- $element->appendChild(new Element('div'));
195
- }
196
-
197
- public function testAppendChild()
198
- {
199
- $list = new Element('ul');
200
-
201
- $this->assertEquals(0, $list->getNode()->childNodes->length);
202
-
203
- $item = new Element('li', 'foo');
204
- $appendedChild = $list->appendChild($item);
205
-
206
- $this->assertEquals(1, $list->getNode()->childNodes->length);
207
- $this->assertInstanceOf('DiDom\Element', $appendedChild);
208
- $this->assertEquals('foo', $appendedChild->getNode()->textContent);
209
-
210
- $item = new Element('li', 'bar');
211
- $appendedChild = $list->appendChild($item);
212
-
213
- $this->assertEquals(2, $list->getNode()->childNodes->length);
214
- $this->assertInstanceOf('DiDom\Element', $appendedChild);
215
- $this->assertEquals('bar', $appendedChild->getNode()->textContent);
216
-
217
- $this->assertEquals('foo', $list->getNode()->childNodes->item(0)->textContent);
218
- $this->assertEquals('bar', $list->getNode()->childNodes->item(1)->textContent);
219
- }
220
-
221
- public function testAppendChildWithArray()
222
- {
223
- $list = new Element('ul');
224
-
225
- $appendedChild = $list->appendChild(new Element('li', 'foo'));
226
-
227
- $this->assertEquals(1, $list->getNode()->childNodes->length);
228
- $this->assertInstanceOf('DiDom\Element', $appendedChild);
229
- $this->assertEquals('foo', $appendedChild->getNode()->textContent);
230
-
231
- $items = [];
232
-
233
- $items[] = new Element('li', 'bar');
234
- $items[] = new Element('li', 'baz');
235
-
236
- $appendedChildren = $list->appendChild($items);
237
-
238
- $this->assertCount(2, $appendedChildren);
239
- $this->assertEquals(3, $list->getNode()->childNodes->length);
240
-
241
- foreach ($appendedChildren as $appendedChild) {
242
- $this->assertInstanceOf('DiDom\Element', $appendedChild);
243
- }
244
-
245
- foreach (['foo', 'bar', 'baz'] as $index => $value) {
246
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
247
- }
248
- }
249
-
250
- /**
251
- * @expectedException \InvalidArgumentException
252
- */
253
- public function testInsertBeforeWithInvalidNodeArgument()
254
- {
255
- $list = new Element('ul');
256
-
257
- $list->insertBefore('foo');
258
- }
259
-
260
- /**
261
- * @expectedException \InvalidArgumentException
262
- * @expectedExceptionMessage Argument 2 passed to DiDom\Element::insertBefore must be an instance of DiDom\Element or DOMNode, string given
263
- */
264
- public function testInsertBeforeWithInvalidReferenceNodeArgument()
265
- {
266
- $list = new Element('ul');
267
-
268
- $list->insertBefore(new Element('li', 'foo'), 'foo');
269
- }
270
-
271
- /**
272
- * @expectedException \LogicException
273
- * @expectedExceptionMessage Can not insert child to element without owner document
274
- */
275
- public function testInsertBeforeWithoutParentNode()
276
- {
277
- $list = new Element(new \DOMElement('ul'));
278
-
279
- $list->insertBefore(new Element('li', 'foo'));
280
- }
281
-
282
- public function testInsertBefore()
283
- {
284
- $list = new Element('ul');
285
-
286
- $insertedNode = $list->insertBefore(new Element('li', 'qux'));
287
-
288
- $this->assertInstanceOf('DiDom\Element', $insertedNode);
289
- $this->assertEquals('qux', $insertedNode->getNode()->textContent);
290
-
291
- foreach (['qux'] as $index => $value) {
292
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
293
- }
294
-
295
- $list->insertBefore(new Element('li', 'foo'), $list->getNode()->childNodes->item(0));
296
-
297
- foreach (['foo', 'qux'] as $index => $value) {
298
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
299
- }
300
-
301
- $list->insertBefore(new Element('li', 'baz'), $list->getNode()->childNodes->item(1));
302
-
303
- foreach (['foo', 'baz', 'qux'] as $index => $value) {
304
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
305
- }
306
-
307
- $list->insertBefore(new Element('li', 'bar'), $list->getNode()->childNodes->item(1));
308
-
309
- foreach (['foo', 'bar', 'baz', 'qux'] as $index => $value) {
310
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
311
- }
312
- }
313
-
314
- public function testInsertBeforeWithoutReferenceNode()
315
- {
316
- $list = new Element('ul');
317
-
318
- $list->insertBefore(new Element('li', 'foo'));
319
- $list->insertBefore(new Element('li', 'bar'));
320
-
321
- foreach (['foo', 'bar'] as $index => $value) {
322
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
323
- }
324
- }
325
-
326
- /**
327
- * @expectedException \InvalidArgumentException
328
- */
329
- public function testInsertAfterWithInvalidNodeArgument()
330
- {
331
- $list = new Element('ul');
332
-
333
- $list->insertAfter('foo');
334
- }
335
-
336
- /**
337
- * @expectedException \InvalidArgumentException
338
- * @expectedExceptionMessage Argument 2 passed to DiDom\Element::insertAfter must be an instance of DiDom\Element or DOMNode, string given
339
- */
340
- public function testInsertAfterWithInvalidReferenceNodeArgument()
341
- {
342
- $list = new Element('ul');
343
-
344
- $list->insertAfter(new Element('li', 'foo'), 'foo');
345
- }
346
-
347
- /**
348
- * @expectedException \LogicException
349
- * @expectedExceptionMessage Can not insert child to element without owner document
350
- */
351
- public function testInsertAfterWithoutParentNode()
352
- {
353
- $list = new Element(new \DOMElement('ul'));
354
-
355
- $list->insertAfter(new Element('li', 'foo'));
356
- }
357
-
358
- public function testInsertAfter()
359
- {
360
- $list = new Element('ul');
361
-
362
- $insertedNode = $list->insertAfter(new Element('li', 'foo'));
363
-
364
- $this->assertInstanceOf('DiDom\Element', $insertedNode);
365
- $this->assertEquals('foo', $insertedNode->getNode()->textContent);
366
-
367
- foreach (['foo'] as $index => $value) {
368
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
369
- }
370
-
371
- $list->insertAfter(new Element('li', 'qux'), $list->getNode()->childNodes->item(0));
372
-
373
- foreach (['foo', 'qux'] as $index => $value) {
374
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
375
- }
376
-
377
- $list->insertAfter(new Element('li', 'bar'), $list->getNode()->childNodes->item(0));
378
-
379
- foreach (['foo', 'bar', 'qux'] as $index => $value) {
380
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
381
- }
382
-
383
- $list->insertAfter(new Element('li', 'baz'), $list->getNode()->childNodes->item(1));
384
-
385
- foreach (['foo', 'bar', 'baz', 'qux'] as $index => $value) {
386
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
387
- }
388
- }
389
-
390
- public function testInsertAfterWithoutReferenceNode()
391
- {
392
- $list = new Element('ul');
393
-
394
- $list->insertAfter(new Element('li', 'foo'));
395
- $list->insertAfter(new Element('li', 'bar'));
396
-
397
- foreach (['foo', 'bar'] as $index => $value) {
398
- $this->assertEquals($value, $list->getNode()->childNodes->item($index)->textContent);
399
- }
400
- }
401
-
402
- public function testHas()
403
- {
404
- $document = new \DOMDocument();
405
- $document->loadHTML('<div><span class="foo">bar</span></div>');
406
-
407
- $node = $document->getElementsByTagName('div')->item(0);
408
- $element = new Element($node);
409
-
410
- $this->assertTrue($element->has('.foo'));
411
- $this->assertFalse($element->has('.bar'));
412
- }
413
-
414
- /**
415
- * @dataProvider findTests
416
- */
417
- public function testFind($html, $selector, $type, $count)
418
- {
419
- $document = new \DOMDocument();
420
- $document->loadHTML($html);
421
-
422
- $domElement = $document->getElementsByTagName('body')->item(0);
423
- $element = new Element($domElement);
424
-
425
- $elements = $element->find($selector, $type);
426
-
427
- $this->assertTrue(is_array($elements));
428
- $this->assertEquals($count, count($elements));
429
-
430
- foreach ($elements as $element) {
431
- $this->assertInstanceOf('DiDom\Element', $element);
432
- }
433
- }
434
-
435
- /**
436
- * @expectedException \LogicException
437
- */
438
- public function testFindInDocumentWithoutOwnerDocument()
439
- {
440
- $element = new Element(new \DOMElement('div'));
441
-
442
- $element->findInDocument('.foo');
443
- }
444
-
445
- public function testFindInDocument()
446
- {
447
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
448
-
449
- $document = new Document($html);
450
-
451
- $items = $document->find('li');
452
- $list = $document->first('ul');
453
-
454
- foreach ($list->find('li') as $index => $item) {
455
- $this->assertFalse($item->is($items[$index]));
456
- }
457
-
458
- foreach ($list->findInDocument('li') as $index => $item) {
459
- $this->assertTrue($item->is($items[$index]));
460
- }
461
-
462
- $this->assertCount(3, $document->find('li'));
463
-
464
- $list->findInDocument('li')[0]->remove();
465
-
466
- $this->assertCount(2, $document->find('li'));
467
- }
468
-
469
- /**
470
- * @expectedException \LogicException
471
- */
472
- public function testFirstInDocumentWithoutOwnerDocument()
473
- {
474
- $element = new Element(new \DOMElement('div'));
475
-
476
- $element->firstInDocument('.foo');
477
- }
478
-
479
- public function testFirstInDocument()
480
- {
481
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
482
-
483
- $document = new Document($html);
484
-
485
- $item = $document->first('li');
486
- $list = $document->first('ul');
487
-
488
- $this->assertFalse($item->is($list->first('li')));
489
- $this->assertTrue($item->is($list->firstInDocument('li')));
490
-
491
- $this->assertCount(3, $document->find('li'));
492
-
493
- $list->findInDocument('li')[0]->remove();
494
-
495
- $this->assertCount(2, $document->find('li'));
496
- }
497
-
498
- public function testFirst()
499
- {
500
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
501
-
502
- $document = new Document($html);
503
-
504
- $list = $document->first('ul');
505
-
506
- $item = $list->getNode()->childNodes->item(0);
507
-
508
- $this->assertEquals($item, $list->first('li')->getNode());
509
-
510
- $list = new Element('ul');
511
-
512
- $this->assertNull($list->first('li'));
513
- }
514
-
515
- /**
516
- * @dataProvider findTests
517
- */
518
- public function testFindAndReturnDomElement($html, $selector, $type, $count)
519
- {
520
- $document = new \DOMDocument();
521
- $document->loadHTML($html);
522
-
523
- $node = $document->getElementsByTagName('body')->item(0);
524
- $element = new Element($node);
525
-
526
- $elements = $element->find($selector, $type, false);
527
-
528
- $this->assertTrue(is_array($elements));
529
- $this->assertEquals($count, count($elements));
530
-
531
- foreach ($elements as $element) {
532
- $this->assertInstanceOf('DOMElement', $element);
533
- }
534
- }
535
-
536
- public function findTests()
537
- {
538
- $html = $this->loadFixture('posts.html');
539
-
540
- return array(
541
- array($html, '.post h2', Query::TYPE_CSS, 3),
542
- array($html, '.fake h2', Query::TYPE_CSS, 0),
543
- array($html, '.post h2, .post p', Query::TYPE_CSS, 6),
544
- array($html, "//*[contains(concat(' ', normalize-space(@class), ' '), ' post ')]", Query::TYPE_XPATH, 3),
545
- );
546
- }
547
-
548
- public function testXpath()
549
- {
550
- $html = $this->loadFixture('posts.html');
551
-
552
- $document = new \DOMDocument();
553
- $document->loadHTML($html);
554
-
555
- $node = $document->getElementsByTagName('body')->item(0);
556
- $element = new Element($node);
557
-
558
- $elements = $element->xpath("//*[contains(concat(' ', normalize-space(@class), ' '), ' post ')]");
559
-
560
- $this->assertTrue(is_array($elements));
561
- $this->assertEquals(3, count($elements));
562
-
563
- foreach ($elements as $element) {
564
- $this->assertInstanceOf('DiDom\Element', $element);
565
- }
566
- }
567
-
568
- public function testCount()
569
- {
570
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
571
-
572
- $document = new Document($html, false);
573
- $list = $document->first('ul');
574
-
575
- $this->assertEquals(3, $list->count('li'));
576
-
577
- $document = new Element('ul');
578
-
579
- $this->assertEquals(0, $document->count('li'));
580
- }
581
-
582
- /**
583
- * @expectedException \InvalidArgumentException
584
- */
585
- public function testMatchesWithInvalidSelectorType()
586
- {
587
- $element = new Element('p');
588
-
589
- $element->matches(null);
590
- }
591
-
592
- /**
593
- * @expectedException \RuntimeException
594
- */
595
- public function testMatchesStrictWithoutTagName()
596
- {
597
- $element = new Element('ul', null, ['id' => 'foo', 'class' => 'bar baz']);
598
-
599
- $element->matches('#foo.bar.baz', true);
600
- }
601
-
602
- public function testMatches()
603
- {
604
- $element = new Element('ul', null, ['id' => 'foo', 'class' => 'bar baz']);
605
-
606
- $this->assertTrue($element->matches('ul'));
607
- $this->assertTrue($element->matches('#foo'));
608
- $this->assertTrue($element->matches('.bar'));
609
- $this->assertTrue($element->matches('ul#foo.bar.baz'));
610
- $this->assertFalse($element->matches('a#foo.bar.baz'));
611
-
612
- // strict
613
- $this->assertTrue($element->matches('ul#foo.bar.baz', true));
614
- $this->assertFalse($element->matches('ul#foo.bar', true));
615
- $this->assertFalse($element->matches('ul#foo', true));
616
- $this->assertFalse($element->matches('ul.bar.baz', true));
617
- $this->assertFalse($element->matches('ul.bar.baz', true));
618
-
619
- $element = new Element('p');
620
-
621
- $this->assertTrue($element->matches('p', true));
622
-
623
- $html = '<!DOCTYPE html>
624
- <html lang="en">
625
- <head>
626
- <meta charset="UTF-8">
627
- <title>Document</title>
628
- </head>
629
- <body>
630
- <a href="#"><img src="foo.jpg" alt="Foo"></a>
631
- </body>
632
- </html>';
633
-
634
- $document = new Document($html, false);
635
- $anchor = $document->first('a');
636
-
637
- $this->assertTrue($anchor->matches('a:has(img[src$=".jpg"])'));
638
- $this->assertTrue($anchor->matches('a img'));
639
- $this->assertFalse($anchor->matches('a img[alt="Bar"]'));
640
- $this->assertFalse($anchor->matches('img'));
641
-
642
- $textNode = new \DOMText('Foo');
643
- $element = new Element($textNode);
644
-
645
- $this->assertFalse($element->matches('#foo'));
646
-
647
- $commentNode = new \DOMComment('Foo');
648
- $element = new Element($commentNode);
649
-
650
- $this->assertFalse($element->matches('#foo'));
651
- }
652
-
653
- public function testHasAttribute()
654
- {
655
- $node = $this->createDomElement('input');
656
- $element = new Element($node);
657
-
658
- $this->assertFalse($element->hasAttribute('value'));
659
-
660
- $node->setAttribute('value', 'test');
661
-
662
- $this->assertTrue($element->hasAttribute('value'));
663
- }
664
-
665
- /**
666
- * @expectedException \InvalidArgumentException
667
- */
668
- public function testSetAttributeWithInvalidValue()
669
- {
670
- $element = new Element('input');
671
- $element->setAttribute('value', []);
672
- }
673
-
674
- public function testSetAttribute()
675
- {
676
- $node = $this->createDomElement('input');
677
-
678
- $element = new Element($node);
679
-
680
- $element->setAttribute('value', 'foo');
681
- $this->assertEquals('foo', $element->getNode()->getAttribute('value'));
682
-
683
- $element->setAttribute('value', 10);
684
- $this->assertEquals('10', $element->getNode()->getAttribute('value'));
685
-
686
- $element->setAttribute('value', 3.14);
687
- $this->assertEquals('3.14', $element->getNode()->getAttribute('value'));
688
- }
689
-
690
- public function testGetAttribute()
691
- {
692
- $node = $this->createDomElement('input');
693
-
694
- $element = new Element($node);
695
-
696
- $this->assertEquals(null, $element->getAttribute('value'));
697
- $this->assertEquals('default', $element->getAttribute('value', 'default'));
698
-
699
- $node->setAttribute('value', 'test');
700
-
701
- $this->assertEquals('test', $element->getAttribute('value'));
702
- }
703
-
704
- public function testRemoveAttribute()
705
- {
706
- $domElement = $this->createDomElement('input', null, ['name' => 'username']);
707
-
708
- $element = new Element($domElement);
709
-
710
- $this->assertTrue($element->getNode()->hasAttribute('name'));
711
-
712
- $result = $element->removeAttribute('name');
713
-
714
- $this->assertEquals(0, $element->getNode()->attributes->length);
715
- $this->assertFalse($element->getNode()->hasAttribute('name'));
716
- $this->assertEquals($result, $element);
717
- }
718
-
719
- public function testRemoveAllAttributes()
720
- {
721
- $attributes = ['type' => 'text', 'name' => 'username'];
722
-
723
- $domElement = $this->createDomElement('input', null, $attributes);
724
-
725
- $element = new Element($domElement);
726
-
727
- $result = $element->removeAllAttributes();
728
-
729
- $this->assertEquals(0, $element->getNode()->attributes->length);
730
- $this->assertEquals($result, $element);
731
- }
732
-
733
- public function testRemoveAllAttributesWithExclusion()
734
- {
735
- $attributes = ['type' => 'text', 'name' => 'username'];
736
-
737
- $domElement = $this->createDomElement('input', null, $attributes);
738
-
739
- $element = new Element($domElement);
740
-
741
- $element->removeAllAttributes(['name']);
742
-
743
- $this->assertEquals(1, $element->getNode()->attributes->length);
744
- $this->assertEquals('username', $element->getNode()->getAttribute('name'));
745
- }
746
-
747
- public function testAttrSet()
748
- {
749
- $element = new Element('input');
750
-
751
- $element->attr('name', 'username');
752
-
753
- $this->assertEquals('username', $element->getNode()->getAttribute('name'));
754
- }
755
-
756
- public function testAttrGet()
757
- {
758
- $element = new Element('input', null, ['name' => 'username']);
759
-
760
- $this->assertEquals('username', $element->attr('name'));
761
- }
762
-
763
- public function testAttributes()
764
- {
765
- $attributes = ['type' => 'text', 'name' => 'username', 'value' => 'John'];
766
-
767
- $domElement = $this->createDomElement('input', null, $attributes);
768
-
769
- $element = new Element($domElement);
770
-
771
- $this->assertEquals($attributes, $element->attributes());
772
- $this->assertEquals(['name' => 'username', 'value' => 'John'], $element->attributes(['name', 'value']));
773
- }
774
-
775
-
776
- public function testAttributesWithText()
777
- {
778
- $element = new Element(new \DOMText('Foo'));
779
-
780
- $this->assertNull($element->attributes());
781
- }
782
-
783
-
784
- public function testAttributesWithComment()
785
- {
786
- $element = new Element(new \DOMComment('Foo'));
787
-
788
- $this->assertNull($element->attributes());
789
- }
790
-
791
- /**
792
- * @expectedException \LogicException
793
- * @expectedExceptionMessage Style attribute is available only for element nodes
794
- */
795
- public function testStyleWithTextNode()
796
- {
797
- $element = new Element(new \DOMText('foo'));
798
-
799
- $element->style();
800
- }
801
-
802
- /**
803
- * @expectedException \LogicException
804
- * @expectedExceptionMessage Style attribute is available only for element nodes
805
- */
806
- public function testStyleWithCommentNode()
807
- {
808
- $element = new Element(new \DOMComment('foo'));
809
-
810
- $element->style();
811
- }
812
-
813
- public function testStyle()
814
- {
815
- $element = new Element('div');
816
-
817
- $styleAttribute = $element->style();
818
-
819
- $this->assertInstanceOf('DiDom\\StyleAttribute', $styleAttribute);
820
- $this->assertSame($element, $styleAttribute->getElement());
821
-
822
- $this->assertSame($styleAttribute, $element->style());
823
-
824
- $element2 = new Element('div');
825
-
826
- $this->assertNotSame($element->style(), $element2->style());
827
- }
828
-
829
- public function testHtml()
830
- {
831
- $element = new Element('span', 'hello');
832
-
833
- $this->assertEquals('<span>hello</span>', $element->html());
834
- }
835
-
836
- public function testOuterHtml()
837
- {
838
- $innerHtml = 'Plain text <span>Lorem ipsum.</span><span>Lorem ipsum.</span>';
839
- $html = "<div id=\"foo\" class=\"bar baz\">$innerHtml</div>";
840
-
841
- $document = new Document($html);
842
-
843
- $this->assertEquals('<div id="foo" class="bar baz"></div>', $document->first('#foo')->outerHtml());
844
- }
845
-
846
- public function testInnerHtml()
847
- {
848
- $innerHtml = ' Plain text <span>Lorem ipsum.</span><span>Lorem ipsum.</span>';
849
- $html = "<div id=\"root\">$innerHtml</div>";
850
-
851
- $document = new Document($html);
852
-
853
- $this->assertEquals($innerHtml, $document->first('#root')->innerHtml());
854
-
855
- $html = '
856
- <!DOCTYPE html>
857
- <html lang="en">
858
- <head>
859
- <meta charset="UTF-8">
860
- <title>Document</title>
861
- </head>
862
- <body>
863
- English language <br>
864
- Русский язык <br>
865
- اللغة العربية <br>
866
- 漢語 <br>
867
- Tiếng Việt <br>
868
-
869
- &lt; &gt;
870
- </body>
871
- </html>
872
- ';
873
-
874
- $expectedContent = '
875
- English language <br>
876
- Русский язык <br>
877
- اللغة العربية <br>
878
- 漢語 <br>
879
- Tiếng Việt <br>
880
-
881
- &lt; &gt;
882
- ';
883
-
884
- $document = new Document($html);
885
-
886
- $this->assertEquals($expectedContent, $document->first('body')->innerHtml());
887
- }
888
-
889
- public function testInnerHtmlOnXmlElement()
890
- {
891
- $innerXml = 'Plain text <span>Lorem <single-tag/> ipsum.</span><span>Lorem ipsum.</span>';
892
- $xml = "<div id=\"root\">$innerXml</div>";
893
-
894
- $document = new Document($xml, false, 'UTF-8', Document::TYPE_XML);
895
-
896
- $expectedXml = 'Plain text <span>Lorem <single-tag></single-tag> ipsum.</span><span>Lorem ipsum.</span>';
897
-
898
- $this->assertEquals($expectedXml, $document->first('#root')->innerHtml());
899
- }
900
-
901
- public function testInnerXml()
902
- {
903
- $innerXml = 'Plain text <span>Lorem <single-tag/> ipsum.</span><span>Lorem ipsum.</span>';
904
- $xml = "<div id=\"root\">$innerXml</div>";
905
-
906
- $document = new Document($xml, false, 'UTF-8', Document::TYPE_XML);
907
-
908
- $this->assertEquals($innerXml, $document->first('#root')->innerXml());
909
- }
910
-
911
- public function testSetInnerHtml()
912
- {
913
- $list = new Element('ul');
914
-
915
- $html = '<li>One</li><li>Two</li><li>Three</li>';
916
-
917
- $this->assertEquals($list, $list->setInnerHtml($html));
918
- $this->assertEquals(['One', 'Two', 'Three'], $list->find('li::text'));
919
-
920
- // check inner HTML rewrite works
921
-
922
- $html = '<li>Foo</li><li>Bar</li><li>Baz</li>';
923
-
924
- $this->assertEquals($list, $list->setInnerHtml($html));
925
- $this->assertEquals(['Foo', 'Bar', 'Baz'], $list->find('li::text'));
926
-
927
- $html = '<div id="root"></div>';
928
- $innerHtml = ' Plain text <span>Lorem ipsum.</span><span>Lorem ipsum.</span>';
929
-
930
- $document = new Document($html, false);
931
-
932
- $document->first('#root')->setInnerHtml($innerHtml);
933
-
934
- $this->assertEquals($innerHtml, $document->first('#root')->innerHtml());
935
- }
936
-
937
- public function testXml()
938
- {
939
- $element = new Element('span', 'hello');
940
-
941
- $prolog = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
942
-
943
- $this->assertEquals($prolog.'<span>hello</span>', $element->xml());
944
- }
945
-
946
- public function testXmlWithOptions()
947
- {
948
- $html = '<html><body><span></span></body></html>';
949
-
950
- $document = new Document();
951
- $document->loadHtml($html);
952
-
953
- $element = $document->find('span')[0];
954
-
955
- $prolog = '<?xml version="1.0" encoding="UTF-8"?>'."\n";
956
-
957
- $this->assertEquals($prolog.'<span/>', $element->xml());
958
- $this->assertEquals($prolog.'<span></span>', $element->xml(LIBXML_NOEMPTYTAG));
959
- }
960
-
961
- public function testGetText()
962
- {
963
- $element = new Element('span', 'hello');
964
-
965
- $this->assertEquals('hello', $element->text());
966
- }
967
-
968
- public function testSetValue()
969
- {
970
- $element = new Element('span', 'hello');
971
- $element->setValue('test');
972
-
973
- $this->assertEquals('test', $element->text());
974
- }
975
-
976
- public function testIsElementNode()
977
- {
978
- $element = new Element('div');
979
-
980
- $element->setInnerHtml(' Foo <span>Bar</span><!-- Baz -->');
981
-
982
- $children = $element->children();
983
-
984
- $this->assertFalse($children[0]->isElementNode());
985
- $this->assertTrue($children[1]->isElementNode());
986
- $this->assertFalse($children[2]->isElementNode());
987
- }
988
-
989
- public function testIsTextNode()
990
- {
991
- $element = new Element('div');
992
-
993
- $element->setInnerHtml(' Foo <span>Bar</span><!-- Baz -->');
994
-
995
- $children = $element->children();
996
-
997
- $this->assertTrue($children[0]->isTextNode());
998
- $this->assertFalse($children[1]->isTextNode());
999
- $this->assertFalse($children[2]->isTextNode());
1000
- }
1001
-
1002
- public function testIsCommentNode()
1003
- {
1004
- $element = new Element('div');
1005
-
1006
- $element->setInnerHtml(' Foo <span>Bar</span><!-- Baz -->');
1007
-
1008
- $children = $element->children();
1009
-
1010
- $this->assertFalse($children[0]->isCommentNode());
1011
- $this->assertFalse($children[1]->isCommentNode());
1012
- $this->assertTrue($children[2]->isCommentNode());
1013
- }
1014
-
1015
- public function testIs()
1016
- {
1017
- $element = new Element('span', 'hello');
1018
- $element2 = new Element('span', 'hello');
1019
-
1020
- $this->assertTrue($element->is($element));
1021
- $this->assertFalse($element->is($element2));
1022
- }
1023
-
1024
- /**
1025
- * @expectedException \InvalidArgumentException
1026
- */
1027
- public function testIsWithInvalidArgument()
1028
- {
1029
- $element = new Element('span', 'hello');
1030
- $element->is(null);
1031
- }
1032
-
1033
- public function testParent()
1034
- {
1035
- $html = $this->loadFixture('posts.html');
1036
- $document = new Document($html, false);
1037
- $element = $document->createElement('span', 'value');
1038
-
1039
- $this->assertEquals($document->getDocument(), $element->getDocument()->getDocument());
1040
- }
1041
-
1042
- public function testClosest()
1043
- {
1044
- // without body and html tags
1045
- $html = '
1046
- <nav>
1047
- <ul class="menu">
1048
- <li><a href="#">Foo</a></li>
1049
- <li><a href="#">Bar</a></li>
1050
- <li><a href="#">Baz</a></li>
1051
- </ul>
1052
- </nav>
1053
- ';
1054
-
1055
- $document = new Document($html);
1056
-
1057
- $menu = $document->first('.menu');
1058
- $link = $document->first('a');
1059
-
1060
- $this->assertNull($link->closest('.unknown-class'));
1061
- $this->assertEquals($menu, $link->closest('.menu'));
1062
-
1063
- $html = '<!DOCTYPE html>
1064
- <html>
1065
- <body>
1066
- <nav></nav>
1067
- <ul class="menu">
1068
- <li><a href="#">Foo</a></li>
1069
- <li><a href="#">Bar</a></li>
1070
- <li><a href="#">Baz</a></li>
1071
- </ul>
1072
- </body>
1073
- </html>';
1074
-
1075
- $document = new Document($html);
1076
-
1077
- $this->assertNull($document->first('ul.menu')->closest('nav'));
1078
- }
1079
-
1080
- // =========================
1081
- // previousSibling
1082
- // =========================
1083
-
1084
- public function testPreviousSibling()
1085
- {
1086
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1087
-
1088
- $document = new Document($html, false);
1089
-
1090
- $list = $document->first('ul')->getNode();
1091
-
1092
- $item = $list->childNodes->item(0);
1093
- $item = new Element($item);
1094
-
1095
- $this->assertNull($item->previousSibling());
1096
-
1097
- $item = $list->childNodes->item(1);
1098
- $item = new Element($item);
1099
-
1100
- $expectedNode = $list->childNodes->item(0);
1101
-
1102
- $this->assertEquals($expectedNode, $item->previousSibling()->getNode());
1103
- }
1104
-
1105
- public function testPreviousSiblingWithTextNode()
1106
- {
1107
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1108
-
1109
- $document = new Document($html, false);
1110
-
1111
- $span = $document->first('span');
1112
-
1113
- $expectedNode = $span->getNode()->previousSibling;
1114
-
1115
- $this->assertEquals($expectedNode, $span->previousSibling()->getNode());
1116
- }
1117
-
1118
- public function testPreviousSiblingWithCommentNode()
1119
- {
1120
- $html = '<p><!-- Foo --><span>Bar</span> Baz</p>';
1121
-
1122
- $document = new Document($html, false);
1123
-
1124
- $span = $document->first('span');
1125
-
1126
- $expectedNode = $span->getNode()->previousSibling;
1127
-
1128
- $this->assertEquals($expectedNode, $span->previousSibling()->getNode());
1129
- }
1130
-
1131
- public function testPreviousSiblingWithSelector()
1132
- {
1133
- $html =
1134
- '<ul>'.
1135
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1136
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1137
- '<li><a href="https://google.com">Google</a></li>'.
1138
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1139
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1140
- '</ul>'
1141
- ;
1142
-
1143
- $document = new Document($html, false);
1144
-
1145
- $list = $document->first('ul');
1146
-
1147
- $item = $list->getNode()->childNodes->item(4);
1148
- $item = new Element($item);
1149
-
1150
- $expectedNode = $list->getNode()->childNodes->item(2);
1151
-
1152
- $this->assertEquals($expectedNode, $item->previousSibling('li:has(a[href$=".com"])')->getNode());
1153
- }
1154
-
1155
- public function testPreviousSiblingWithNodeType()
1156
- {
1157
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1158
-
1159
- $document = new Document($html, false);
1160
-
1161
- $paragraph = $document->first('p');
1162
- $span = $document->find('span')[1];
1163
-
1164
- $expectedNode = $paragraph->getNode()->childNodes->item(1);
1165
- $this->assertEquals($expectedNode, $span->previousSibling(null, 'DOMElement')->getNode());
1166
-
1167
- $expectedNode = $paragraph->getNode()->childNodes->item(2);
1168
- $this->assertEquals($expectedNode, $span->previousSibling(null, 'DOMComment')->getNode());
1169
- }
1170
-
1171
- /**
1172
- * @expectedException \InvalidArgumentException
1173
- */
1174
- public function testPreviousSiblingWithInvalidTypeOfNodeTypeArgument()
1175
- {
1176
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1177
-
1178
- $document = new Document($html, false);
1179
-
1180
- $span = $document->find('span')[1];
1181
-
1182
- $span->previousSibling(null, []);
1183
- }
1184
-
1185
- /**
1186
- * @expectedException \RuntimeException
1187
- */
1188
- public function testPreviousSiblingWithInvalidNodeType()
1189
- {
1190
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1191
-
1192
- $document = new Document($html, false);
1193
-
1194
- $span = $document->find('span')[1];
1195
-
1196
- $span->previousSibling(null, 'foo');
1197
- }
1198
-
1199
- /**
1200
- * @dataProvider previousSiblingWithSelectorAndNotDomElementNodeTypeDataProvider
1201
- *
1202
- * @expectedException \LogicException
1203
- */
1204
- public function testPreviousSiblingWithSelectorAndNotDomElement($nodeType)
1205
- {
1206
- $html =
1207
- '<ul>'.
1208
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1209
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1210
- '<li><a href="https://google.com">Google</a></li>'.
1211
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1212
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1213
- '</ul>'
1214
- ;
1215
-
1216
- $document = new Document($html, false);
1217
-
1218
- $list = $document->first('ul');
1219
-
1220
- $item = $list->getNode()->childNodes->item(4);
1221
- $item = new Element($item);
1222
-
1223
- $item->previousSibling('li:has(a[href$=".com"])', $nodeType);
1224
- }
1225
-
1226
- public function previousSiblingWithSelectorAndNotDomElementNodeTypeDataProvider()
1227
- {
1228
- return [['DOMText'], ['DOMComment']];
1229
- }
1230
-
1231
- // =========================
1232
- // previousSiblings
1233
- // =========================
1234
-
1235
- public function testPreviousSiblings()
1236
- {
1237
- $html = '<p>Foo <span>Bar</span> Baz <span>Qux</span></p>';
1238
-
1239
- $document = new Document($html, false);
1240
-
1241
- $paragraph = $document->first('p');
1242
- $span = $paragraph->find('span')[1];
1243
-
1244
- $childNodes = $paragraph->getNode()->childNodes;
1245
-
1246
- $expectedResult = [
1247
- $childNodes->item(0),
1248
- $childNodes->item(1),
1249
- $childNodes->item(2),
1250
- ];
1251
-
1252
- $previousSiblings = $span->previousSiblings();
1253
-
1254
- $this->assertCount(count($expectedResult), $previousSiblings);
1255
-
1256
- foreach ($previousSiblings as $index => $previousSibling) {
1257
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1258
- }
1259
- }
1260
-
1261
- public function testPreviousSiblingsWithSelector()
1262
- {
1263
- $html =
1264
- '<ul>'.
1265
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1266
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1267
- '<li><a href="https://google.com">Google</a></li>'.
1268
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1269
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1270
- '</ul>'
1271
- ;
1272
-
1273
- $document = new Document($html, false);
1274
-
1275
- $list = $document->first('ul');
1276
-
1277
- $item = $list->getNode()->childNodes->item(4);
1278
- $item = new Element($item);
1279
-
1280
- $childNodes = $list->getNode()->childNodes;
1281
-
1282
- $expectedResult = [
1283
- $childNodes->item(0),
1284
- $childNodes->item(1),
1285
- $childNodes->item(2),
1286
- ];
1287
-
1288
- $previousSiblings = $item->previousSiblings('li:has(a[href$=".com"])');
1289
-
1290
- $this->assertCount(count($expectedResult), $previousSiblings);
1291
-
1292
- foreach ($previousSiblings as $index => $previousSibling) {
1293
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1294
- }
1295
- }
1296
-
1297
- public function testPreviousSiblingsWithNodeType()
1298
- {
1299
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1300
-
1301
- $document = new Document($html, false);
1302
-
1303
- $paragraph = $document->first('p');
1304
- $span = $document->find('span')[1];
1305
-
1306
- $childNodes = $paragraph->getNode()->childNodes;
1307
-
1308
- $expectedResult = [
1309
- $childNodes->item(1),
1310
- ];
1311
-
1312
- $previousSiblings = $span->previousSiblings(null, 'DOMElement');
1313
-
1314
- $this->assertCount(count($expectedResult), $previousSiblings);
1315
-
1316
- foreach ($previousSiblings as $index => $previousSibling) {
1317
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1318
- }
1319
-
1320
- $expectedResult = [
1321
- $childNodes->item(2),
1322
- ];
1323
-
1324
- $previousSiblings = $span->previousSiblings(null, 'DOMComment');
1325
-
1326
- $this->assertCount(count($expectedResult), $previousSiblings);
1327
-
1328
- foreach ($previousSiblings as $index => $previousSibling) {
1329
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1330
- }
1331
- }
1332
-
1333
- /**
1334
- * @expectedException \InvalidArgumentException
1335
- */
1336
- public function testPreviousSiblingsWithInvalidTypeOfNodeTypeArgument()
1337
- {
1338
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1339
-
1340
- $document = new Document($html, false);
1341
-
1342
- $span = $document->find('span')[1];
1343
-
1344
- $span->previousSiblings(null, []);
1345
- }
1346
-
1347
- /**
1348
- * @expectedException \RuntimeException
1349
- */
1350
- public function testPreviousSiblingsWithInvalidNodeType()
1351
- {
1352
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1353
-
1354
- $document = new Document($html, false);
1355
-
1356
- $span = $document->find('span')[1];
1357
-
1358
- $span->previousSibling(null, 'foo');
1359
- }
1360
-
1361
- /**
1362
- * @dataProvider previousSiblingsWithSelectorAndNotDomElementNodeTypeDataProvider
1363
- *
1364
- * @expectedException \LogicException
1365
- */
1366
- public function testPreviousSiblingsWithSelectorAndNotDomElement($nodeType)
1367
- {
1368
- $html =
1369
- '<ul>'.
1370
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1371
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1372
- '<li><a href="https://google.com">Google</a></li>'.
1373
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1374
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1375
- '</ul>'
1376
- ;
1377
-
1378
- $document = new Document($html, false);
1379
-
1380
- $list = $document->first('ul');
1381
-
1382
- $item = $list->getNode()->childNodes->item(4);
1383
- $item = new Element($item);
1384
-
1385
- $item->previousSiblings('li:has(a[href$=".com"])', $nodeType);
1386
- }
1387
-
1388
- public function previousSiblingsWithSelectorAndNotDomElementNodeTypeDataProvider()
1389
- {
1390
- return [['DOMText'], ['DOMComment']];
1391
- }
1392
-
1393
- // =========================
1394
- // nextSibling
1395
- // =========================
1396
-
1397
- public function testNextSibling()
1398
- {
1399
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1400
-
1401
- $document = new Document($html, false);
1402
-
1403
- $list = $document->first('ul');
1404
-
1405
- $item = $list->getNode()->childNodes->item(2);
1406
- $item = new Element($item);
1407
-
1408
- $this->assertNull($item->nextSibling());
1409
-
1410
- $item = $list->getNode()->childNodes->item(0);
1411
- $item = new Element($item);
1412
-
1413
- $expectedNode = $list->getNode()->childNodes->item(1);
1414
-
1415
- $this->assertEquals($expectedNode, $item->nextSibling()->getNode());
1416
- }
1417
-
1418
- public function testNextSiblingWithTextNode()
1419
- {
1420
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1421
-
1422
- $document = new Document($html, false);
1423
-
1424
- $paragraph = $document->first('p');
1425
- $span = $paragraph->first('span');
1426
-
1427
- $expectedNode = $span->getNode()->nextSibling;
1428
-
1429
- $this->assertEquals($expectedNode, $span->nextSibling()->getNode());
1430
- }
1431
-
1432
- public function testNextSiblingWithCommentNode()
1433
- {
1434
- $html = '<p>Foo <span>Bar</span><!-- Baz --></p>';
1435
-
1436
- $document = new Document($html, false);
1437
-
1438
- $paragraph = $document->first('p');
1439
- $span = $paragraph->first('span');
1440
-
1441
- $expectedNode = $span->getNode()->nextSibling;
1442
-
1443
- $this->assertEquals($expectedNode, $span->nextSibling()->getNode());
1444
- }
1445
-
1446
- public function testNextSiblingWithSelector()
1447
- {
1448
- $html =
1449
- '<ul>'.
1450
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1451
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1452
- '<li><a href="https://google.com">Google</a></li>'.
1453
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1454
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1455
- '</ul>'
1456
- ;
1457
-
1458
- $document = new Document($html, false);
1459
-
1460
- $list = $document->first('ul');
1461
-
1462
- $item = $list->getNode()->childNodes->item(0);
1463
- $item = new Element($item);
1464
-
1465
- $expectedNode = $list->getNode()->childNodes->item(3);
1466
-
1467
- $this->assertEquals($expectedNode, $item->nextSibling('li:has(a[href$=".org"])')->getNode());
1468
- }
1469
-
1470
- public function testNextSiblingWithNodeType()
1471
- {
1472
- $html = '<p>Foo <span>Bar</span> Baz <!--qwe--><span>Qux</span></p>';
1473
-
1474
- $document = new Document($html, false);
1475
-
1476
- $paragraph = $document->first('p');
1477
- $span = $document->find('span')[0];
1478
-
1479
- $expectedNode = $paragraph->getNode()->childNodes->item(4);
1480
- $this->assertEquals($expectedNode, $span->nextSibling(null, 'DOMElement')->getNode());
1481
-
1482
- $expectedNode = $paragraph->getNode()->childNodes->item(3);
1483
- $this->assertEquals($expectedNode, $span->nextSibling(null, 'DOMComment')->getNode());
1484
- }
1485
-
1486
- /**
1487
- * @expectedException \InvalidArgumentException
1488
- */
1489
- public function testNextSiblingWithInvalidTypeOfNodeTypeArgument()
1490
- {
1491
- $html = '<p>Foo <span>Bar</span> Baz <!--qwe--><span>Qux</span></p>';
1492
-
1493
- $document = new Document($html, false);
1494
-
1495
- $span = $document->find('span')[0];
1496
-
1497
- $span->nextSibling(null, []);
1498
- }
1499
-
1500
- /**
1501
- * @expectedException \RuntimeException
1502
- */
1503
- public function testNextSiblingWithInvalidNodeType()
1504
- {
1505
- $html = '<p>Foo <span>Bar</span> Baz <!--qwe--><span>Qux</span></p>';
1506
-
1507
- $document = new Document($html, false);
1508
-
1509
- $span = $document->find('span')[0];
1510
-
1511
- $span->nextSibling(null, 'foo');
1512
- }
1513
-
1514
- /**
1515
- * @dataProvider nextSiblingWithSelectorAndNotDomElementNodeTypeDataProvider
1516
- *
1517
- * @expectedException \LogicException
1518
- */
1519
- public function testNextSiblingWithSelectorAndNotDomElement($nodeType)
1520
- {
1521
- $html =
1522
- '<ul>'.
1523
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1524
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1525
- '<li><a href="https://google.com">Google</a></li>'.
1526
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1527
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1528
- '</ul>'
1529
- ;
1530
-
1531
- $document = new Document($html, false);
1532
-
1533
- $list = $document->first('ul');
1534
-
1535
- $item = $list->getNode()->childNodes->item(0);
1536
- $item = new Element($item);
1537
-
1538
- $item->nextSibling('li:has(a[href$=".com"])', $nodeType);
1539
- }
1540
-
1541
- public function nextSiblingWithSelectorAndNotDomElementNodeTypeDataProvider()
1542
- {
1543
- return [['DOMText'], ['DOMComment']];
1544
- }
1545
-
1546
- // =========================
1547
- // nextSiblings
1548
- // =========================
1549
-
1550
- public function testNextSiblings()
1551
- {
1552
- $html = '<p>Foo <span>Bar</span> Baz <span>Qux</span></p>';
1553
-
1554
- $document = new Document($html, false);
1555
-
1556
- $paragraph = $document->first('p');
1557
- $span = $paragraph->find('span')[0];
1558
-
1559
- $childNodes = $paragraph->getNode()->childNodes;
1560
-
1561
- $expectedResult = [
1562
- $childNodes->item(2),
1563
- $childNodes->item(3),
1564
- ];
1565
-
1566
- $nextSiblings = $span->nextSiblings();
1567
-
1568
- $this->assertCount(count($expectedResult), $nextSiblings);
1569
-
1570
- foreach ($nextSiblings as $index => $nextSibling) {
1571
- $this->assertEquals($expectedResult[$index], $nextSibling->getNode());
1572
- }
1573
- }
1574
-
1575
- public function testNextSiblingsWithSelector()
1576
- {
1577
- $html =
1578
- '<ul>'.
1579
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1580
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1581
- '<li><a href="https://google.com">Google</a></li>'.
1582
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1583
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1584
- '</ul>'
1585
- ;
1586
-
1587
- $document = new Document($html, false);
1588
-
1589
- $list = $document->first('ul');
1590
-
1591
- $item = $list->getNode()->childNodes->item(0);
1592
- $item = new Element($item);
1593
-
1594
- $childNodes = $list->getNode()->childNodes;
1595
-
1596
- $expectedResult = [
1597
- $childNodes->item(1),
1598
- $childNodes->item(2),
1599
- ];
1600
-
1601
- $nextSiblings = $item->nextSiblings('li:has(a[href$=".com"])');
1602
-
1603
- $this->assertCount(count($expectedResult), $nextSiblings);
1604
-
1605
- foreach ($nextSiblings as $index => $nextSibling) {
1606
- $this->assertEquals($expectedResult[$index], $nextSibling->getNode());
1607
- }
1608
- }
1609
-
1610
- public function testNextSiblingsWithNodeType()
1611
- {
1612
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1613
-
1614
- $document = new Document($html, false);
1615
-
1616
- $paragraph = $document->first('p');
1617
- $span = $document->find('span')[0];
1618
-
1619
- $childNodes = $paragraph->getNode()->childNodes;
1620
-
1621
- $expectedResult = [
1622
- $childNodes->item(4),
1623
- ];
1624
-
1625
- $previousSiblings = $span->nextSiblings(null, 'DOMElement');
1626
-
1627
- $this->assertCount(count($expectedResult), $previousSiblings);
1628
-
1629
- foreach ($previousSiblings as $index => $previousSibling) {
1630
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1631
- }
1632
-
1633
- $expectedResult = [
1634
- $childNodes->item(2),
1635
- ];
1636
-
1637
- $previousSiblings = $span->nextSiblings(null, 'DOMComment');
1638
-
1639
- $this->assertCount(count($expectedResult), $previousSiblings);
1640
-
1641
- foreach ($previousSiblings as $index => $previousSibling) {
1642
- $this->assertEquals($expectedResult[$index], $previousSibling->getNode());
1643
- }
1644
- }
1645
-
1646
- /**
1647
- * @expectedException \InvalidArgumentException
1648
- */
1649
- public function testNextSiblingsWithInvalidTypeOfNodeTypeArgument()
1650
- {
1651
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1652
-
1653
- $document = new Document($html, false);
1654
-
1655
- $span = $document->find('span')[0];
1656
-
1657
- $span->nextSiblings(null, []);
1658
- }
1659
-
1660
- /**
1661
- * @expectedException \RuntimeException
1662
- */
1663
- public function testNextSiblingsWithInvalidNodeType()
1664
- {
1665
- $html = '<p>Foo <span>Bar</span><!--qwe--> Baz <span>Qux</span></p>';
1666
-
1667
- $document = new Document($html, false);
1668
-
1669
- $span = $document->find('span')[0];
1670
-
1671
- $span->nextSiblings(null, 'foo');
1672
- }
1673
-
1674
- /**
1675
- * @dataProvider nextSiblingsWithSelectorAndNotDomElementNodeTypeDataProvider
1676
- *
1677
- * @expectedException \LogicException
1678
- */
1679
- public function testNextSiblingsWithSelectorAndNotDomElement($nodeType)
1680
- {
1681
- $html =
1682
- '<ul>'.
1683
- '<li><a href="https://amazon.com">Amazon</a></li>'.
1684
- '<li><a href="https://facebook.com">Facebook</a></li>'.
1685
- '<li><a href="https://google.com">Google</a></li>'.
1686
- '<li><a href="https://www.w3.org">W3C</a></li>'.
1687
- '<li><a href="https://wikipedia.org">Wikipedia</a></li>'.
1688
- '</ul>'
1689
- ;
1690
-
1691
- $document = new Document($html, false);
1692
-
1693
- $list = $document->first('ul');
1694
-
1695
- $item = $list->getNode()->childNodes->item(0);
1696
- $item = new Element($item);
1697
-
1698
- $item->nextSiblings('li:has(a[href$=".com"])', $nodeType);
1699
- }
1700
-
1701
- public function nextSiblingsWithSelectorAndNotDomElementNodeTypeDataProvider()
1702
- {
1703
- return [['DOMText'], ['DOMComment']];
1704
- }
1705
-
1706
- public function testChild()
1707
- {
1708
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1709
-
1710
- $document = new Document($html, false);
1711
-
1712
- $list = $document->first('ul');
1713
-
1714
- $this->assertEquals($list->getNode()->childNodes->item(0), $list->child(0)->getNode());
1715
- $this->assertEquals($list->getNode()->childNodes->item(2), $list->child(2)->getNode());
1716
- $this->assertNull($list->child(3));
1717
-
1718
- // with text nodes
1719
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1720
-
1721
- $document = new Document($html, false);
1722
-
1723
- $paragraph = $document->first('p');
1724
-
1725
- $child = $paragraph->getNode()->childNodes->item(0);
1726
-
1727
- $this->assertEquals($child, $paragraph->child(0)->getNode());
1728
- }
1729
-
1730
- public function testFirstChild()
1731
- {
1732
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1733
-
1734
- $document = new Document($html, false);
1735
-
1736
- $list = $document->first('ul');
1737
-
1738
- $this->assertEquals($list->getNode()->firstChild, $list->firstChild()->getNode());
1739
-
1740
- $list = new Element('ul');
1741
-
1742
- $this->assertNull($list->firstChild());
1743
-
1744
- // with text nodes
1745
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1746
-
1747
- $document = new Document($html, false);
1748
-
1749
- $paragraph = $document->first('p');
1750
-
1751
- $firstChild = $paragraph->getNode()->firstChild;
1752
-
1753
- $this->assertEquals($firstChild, $paragraph->firstChild()->getNode());
1754
- }
1755
-
1756
- public function testLastChild()
1757
- {
1758
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1759
-
1760
- $document = new Document($html, false);
1761
- $list = $document->first('ul');
1762
-
1763
- $this->assertEquals($list->getNode()->lastChild, $list->lastChild()->getNode());
1764
-
1765
- $list = new Element('ul');
1766
-
1767
- $this->assertNull($list->lastChild());
1768
-
1769
- // with text nodes
1770
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1771
-
1772
- $document = new Document($html, false);
1773
- $paragraph = $document->first('p');
1774
-
1775
- $lastChild = $paragraph->getNode()->lastChild;
1776
-
1777
- $this->assertEquals($lastChild, $paragraph->lastChild()->getNode());
1778
- }
1779
-
1780
- public function testHasChildren()
1781
- {
1782
- $html = '
1783
- <p class="element"><br></p>
1784
- <p class="text">Foo</p>
1785
- <p class="comment"><!-- Foo --></p>
1786
- <p class="empty"></p>
1787
- ';
1788
-
1789
- $document = new Document($html);
1790
-
1791
- $this->assertTrue($document->first('.element')->hasChildren());
1792
- $this->assertTrue($document->first('.text')->hasChildren());
1793
- $this->assertTrue($document->first('.comment')->hasChildren());
1794
- $this->assertFalse($document->first('.empty')->hasChildren());
1795
- }
1796
-
1797
- public function testChildren()
1798
- {
1799
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1800
-
1801
- $document = new Document($html, false);
1802
-
1803
- $list = $document->first('ul');
1804
-
1805
- $children = $list->children();
1806
-
1807
- foreach ($list->getNode()->childNodes as $index => $node) {
1808
- $this->assertEquals($node, $children[$index]->getNode());
1809
- }
1810
-
1811
- // with text nodes
1812
- $html = '<p>Foo <span>Bar</span> Baz</p>';
1813
-
1814
- $document = new Document($html, false);
1815
-
1816
- $paragraph = $document->first('p');
1817
-
1818
- $children = $paragraph->children();
1819
-
1820
- foreach ($paragraph->getNode()->childNodes as $index => $node) {
1821
- $this->assertEquals($node, $children[$index]->getNode());
1822
- }
1823
- }
1824
-
1825
- public function testParentWithoutOwner()
1826
- {
1827
- $element = new Element(new \DOMElement('span', 'hello'));
1828
-
1829
- $this->assertNull($element->parent());
1830
- }
1831
-
1832
- public function testRemoveChild()
1833
- {
1834
- $html = '<div><span>Foo</span></div>';
1835
- $document = new Document($html, false);
1836
-
1837
- $div = $document->first('div');
1838
- $span = $document->first('span');
1839
-
1840
- $this->assertEquals($span->getNode(), $div->removeChild($span)->getNode());
1841
- $this->assertCount(0, $document->find('span'));
1842
- }
1843
-
1844
- public function testRemoveChildren()
1845
- {
1846
- $html = '<div><span>Foo</span>Bar<!-- Baz --></div>';
1847
- $document = new Document($html, false);
1848
-
1849
- $div = $document->first('div');
1850
- $span = $document->first('span');
1851
-
1852
- $childNodes = $div->children();
1853
- $removedNodes = $div->removeChildren();
1854
-
1855
- foreach ($childNodes as $index => $childNode) {
1856
- $this->assertEquals($childNode->getNode(), $removedNodes[$index]->getNode());
1857
- }
1858
-
1859
- $this->assertCount(0, $document->find('span'));
1860
- }
1861
-
1862
- public function testRemove()
1863
- {
1864
- $html = '<div><span>Foo</span></div>';
1865
- $document = new Document($html, false);
1866
-
1867
- $span = $document->first('span');
1868
-
1869
- $this->assertEquals($span->getNode(), $span->remove()->getNode());
1870
- $this->assertCount(0, $document->find('span'));
1871
- }
1872
-
1873
- /**
1874
- * @expectedException \LogicException
1875
- */
1876
- public function testRemoveWithoutParentNode()
1877
- {
1878
- $element = new Element('div', 'Foo');
1879
-
1880
- $element->remove();
1881
- }
1882
-
1883
- public function testReplace()
1884
- {
1885
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1886
-
1887
- $document = new Document($html, false);
1888
-
1889
- $first = $document->find('li')[0];
1890
- $third = $document->find('li')[2];
1891
-
1892
- $this->assertEquals($first->getNode(), $first->replace($third)->getNode());
1893
- $this->assertEquals($third->getNode(), $document->find('li')[0]->getNode());
1894
- $this->assertCount(3, $document->find('li'));
1895
-
1896
- // replace without cloning
1897
- $document = new Document($html, false);
1898
-
1899
- $first = $document->find('li')[0];
1900
- $third = $document->find('li')[2];
1901
-
1902
- $this->assertEquals($first->getNode(), $first->replace($third, false)->getNode());
1903
- $this->assertEquals($third->getNode(), $document->find('li')[0]->getNode());
1904
- $this->assertCount(2, $document->find('li'));
1905
- }
1906
-
1907
- public function testReplaceToNewElement()
1908
- {
1909
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1910
-
1911
- $document = new Document($html, false);
1912
-
1913
- $first = $document->find('li')[0];
1914
-
1915
- $newElement = new Element('li', 'Foo');
1916
-
1917
- $this->assertEquals($first->getNode(), $first->replace($newElement)->getNode());
1918
- $this->assertEquals('Foo', $document->find('li')[0]->text());
1919
- $this->assertCount(3, $document->find('li'));
1920
-
1921
- // replace with new node
1922
- $html = '<span>Foo <a href="#">Bar</a> Baz</span>';
1923
-
1924
- $document = new Document($html, false);
1925
-
1926
- $anchor = $document->first('a');
1927
-
1928
- $textNode = new \DOMText($anchor->text());
1929
-
1930
- $anchor->replace($textNode);
1931
- }
1932
-
1933
- public function testReplaceWithDifferentDocuments()
1934
- {
1935
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1936
-
1937
- $document = new Document($html, false);
1938
- $document2 = new Document($html, false);
1939
-
1940
- $first = $document->find('li')[0];
1941
- $third = $document2->find('li')[2];
1942
-
1943
- $first->replace($third);
1944
- }
1945
-
1946
- /**
1947
- * @expectedException \InvalidArgumentException
1948
- */
1949
- public function testReplaceWithInvalidArgument()
1950
- {
1951
- $html = '<ul><li>One</li><li>Two</li><li>Three</li></ul>';
1952
-
1953
- $document = new Document($html, false);
1954
-
1955
- $document->find('li')[0]->replace(null);
1956
- }
1957
-
1958
- /**
1959
- * @expectedException \LogicException
1960
- */
1961
- public function testReplaceElementWithoutParentNode()
1962
- {
1963
- $element = new Element('div', 'Foo');
1964
-
1965
- $element->replace(new Element('div', 'Bar'));
1966
- }
1967
-
1968
- public function testGetLineNo()
1969
- {
1970
- $element = new Element('div');
1971
-
1972
- $this->assertEquals(0, $element->getLineNo());
1973
-
1974
- $html = '<ul>
1975
- <li>One</li>
1976
- <li>Two</li>
1977
- <li>Three</li>
1978
- </ul>';
1979
-
1980
- $document = new Document($html, false);
1981
-
1982
- $this->assertEquals(4, $document->find('li')[2]->getLineNo());
1983
- }
1984
-
1985
- public function testCloneNode()
1986
- {
1987
- $element = new Element('input');
1988
-
1989
- $cloned = $element->cloneNode(true);
1990
-
1991
- $this->assertFalse($element->is($cloned));
1992
- }
1993
-
1994
- public function testGetNode()
1995
- {
1996
- $node = $this->createDomElement('input');
1997
- $element = new Element($node);
1998
-
1999
- $this->assertEquals($node, $element->getNode());
2000
- }
2001
-
2002
- public function testGetDocument()
2003
- {
2004
- $html = $this->loadFixture('posts.html');
2005
-
2006
- $document = new Document($html, false);
2007
- $element = $document->createElement('span', 'value');
2008
-
2009
- $this->assertEquals($document->getDocument(), $element->getDocument()->getDocument());
2010
- }
2011
-
2012
- public function testToDocument()
2013
- {
2014
- $element = new Element('input');
2015
-
2016
- $document = $element->toDocument();
2017
-
2018
- $this->assertInstanceOf('DiDom\Document', $document);
2019
- $this->assertEquals('UTF-8', $document->getDocument()->encoding);
2020
-
2021
- $document = $element->toDocument('CP1251');
2022
-
2023
- $this->assertEquals('CP1251', $document->getDocument()->encoding);
2024
- }
2025
-
2026
- public function testSetMagicMethod()
2027
- {
2028
- $node = $this->createDomElement('input');
2029
-
2030
- $element = new Element($node);
2031
- $element->name = 'username';
2032
-
2033
- $this->assertEquals('username', $element->getNode()->getAttribute('name'));
2034
- }
2035
-
2036
- public function testGetMagicMethod()
2037
- {
2038
- $element = new Element('input', null, ['name' => 'username']);
2039
-
2040
- $this->assertEquals('username', $element->name);
2041
- }
2042
-
2043
- public function testIssetMagicMethod()
2044
- {
2045
- $node = $this->createDomElement('input');
2046
- $element = new Element($node);
2047
-
2048
- $this->assertFalse(isset($element->value));
2049
-
2050
- $node->setAttribute('value', 'test');
2051
- $element = new Element($node);
2052
-
2053
- $this->assertTrue(isset($element->value));
2054
- }
2055
-
2056
- public function testUnsetMagicMethod()
2057
- {
2058
- $element = new Element('input', null, ['name' => 'username']);
2059
-
2060
- $this->assertTrue($element->hasAttribute('name'));
2061
-
2062
- unset($element->name);
2063
-
2064
- $this->assertFalse($element->hasAttribute('name'));
2065
- }
2066
-
2067
- public function testToString()
2068
- {
2069
- $element = new Element('span', 'hello');
2070
-
2071
- $this->assertEquals($element->html(), $element->__toString());
2072
- }
2073
-
2074
- /**
2075
- * @dataProvider findTests
2076
- */
2077
- public function testInvoke($html, $selector, $type, $count)
2078
- {
2079
- $document = new \DOMDocument();
2080
- $document->loadHTML($html);
2081
-
2082
- $node = $document->getElementsByTagName('body')->item(0);
2083
- $element = new Element($node);
2084
-
2085
- $elements = $element($selector, $type);
2086
-
2087
- $this->assertTrue(is_array($elements));
2088
- $this->assertEquals($count, count($elements));
2089
-
2090
- foreach ($elements as $element) {
2091
- $this->assertInstanceOf('DiDom\Element', $element);
2092
- }
2093
- }
2094
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/DiDom/QueryTest.php DELETED
@@ -1,425 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Query;
6
- use Tests\TestCase;
7
-
8
- class QueryTest extends TestCase
9
- {
10
- /**
11
- * @expectedException \InvalidArgumentException
12
- * @expectedExceptionMessage DiDom\Query::compile expects parameter 1 to be string, NULL given
13
- */
14
- public function testCompileWithNonStringExpression()
15
- {
16
- Query::compile(null);
17
- }
18
-
19
- /**
20
- * @expectedException \InvalidArgumentException
21
- * @expectedExceptionMessage DiDom\Query::compile expects parameter 2 to be string, NULL given
22
- */
23
- public function testCompileWithNonStringExpressionType()
24
- {
25
- Query::compile('h1', null);
26
- }
27
-
28
- /**
29
- * @expectedException \RuntimeException
30
- * @expectedExceptionMessage Unknown expression type "foo"
31
- */
32
- public function testCompileWithUnknownExpressionType()
33
- {
34
- Query::compile('h1', 'foo');
35
- }
36
-
37
- /**
38
- * @dataProvider compileCssTests
39
- */
40
- public function testCompileCssSelector($selector, $xpath)
41
- {
42
- $this->assertEquals($xpath, Query::compile($selector));
43
- }
44
-
45
- /**
46
- * @dataProvider getSegmentsTests
47
- */
48
- public function testGetSegments($selector, $segments)
49
- {
50
- $this->assertEquals($segments, Query::getSegments($selector));
51
- }
52
-
53
- /**
54
- * @dataProvider buildXpathTests
55
- */
56
- public function testBuildXpath($segments, $xpath)
57
- {
58
- $this->assertEquals($xpath, Query::buildXpath($segments));
59
- }
60
-
61
- /**
62
- * @expectedException \InvalidArgumentException
63
- */
64
- public function testBuildXpathWithEmptyArray()
65
- {
66
- Query::buildXpath([]);
67
- }
68
-
69
- /**
70
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
71
- * @expectedExceptionMessage The expression must not be empty
72
- */
73
- public function testCompileWithEmptyXpathExpression()
74
- {
75
- Query::compile('', Query::TYPE_XPATH);
76
- }
77
-
78
- /**
79
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
80
- * @expectedExceptionMessage The expression must not be empty
81
- */
82
- public function testCompileWithEmptyCssExpression()
83
- {
84
- Query::compile('', Query::TYPE_CSS);
85
- }
86
-
87
- /**
88
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
89
- * @expectedExceptionMessage The selector must not be empty
90
- */
91
- public function testGetSegmentsWithEmptySelector()
92
- {
93
- Query::getSegments('');
94
- }
95
-
96
- /**
97
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
98
- * @expectedExceptionMessage Invalid selector "input[=foo]": attribute name must not be empty
99
- */
100
- public function testEmptyAttributeName()
101
- {
102
- Query::compile('input[=foo]');
103
- }
104
-
105
- /**
106
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
107
- * @expectedExceptionMessage Unknown pseudo-class "unknown-pseudo-class"
108
- */
109
- public function testUnknownPseudoClass()
110
- {
111
- Query::compile('li:unknown-pseudo-class');
112
- }
113
-
114
- /**
115
- * @dataProvider containsInvalidCaseSensitiveParameterDataProvider
116
- */
117
- public function testContainsInvalidCaseSensitiveParameter($caseSensitive)
118
- {
119
- $message = sprintf('Parameter 2 of "contains" pseudo-class must be equal true or false, "%s" given', $caseSensitive);
120
-
121
- $this->setExpectedException('DiDom\Exceptions\InvalidSelectorException', $message);
122
-
123
- Query::compile("a:contains('Log in', {$caseSensitive})");
124
- }
125
-
126
- public function containsInvalidCaseSensitiveParameterDataProvider()
127
- {
128
- return [
129
- ['foo'],
130
- ['TRUE'],
131
- ['FALSE'],
132
- ];
133
- }
134
-
135
- /**
136
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
137
- * @expectedExceptionMessage nth-child (or nth-last-child) expression must not be empty
138
- */
139
- public function testEmptyNthExpression()
140
- {
141
- Query::compile('li:nth-child()');
142
- }
143
-
144
- /**
145
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
146
- * @expectedExceptionMessage Invalid property "::"
147
- */
148
- public function testEmptyProperty()
149
- {
150
- Query::compile('li::');
151
- }
152
-
153
- /**
154
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
155
- * @expectedExceptionMessage Unknown property "foo"
156
- */
157
- public function testInvalidProperty()
158
- {
159
- Query::compile('li::foo');
160
- }
161
-
162
- /**
163
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
164
- * @expectedExceptionMessage Invalid nth-child expression "foo"
165
- */
166
- public function testUnknownNthExpression()
167
- {
168
- Query::compile('li:nth-child(foo)');
169
- }
170
-
171
- /**
172
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
173
- * @expectedExceptionMessage Invalid selector "."
174
- */
175
- public function testGetSegmentsWithEmptyClassName()
176
- {
177
- Query::getSegments('.');
178
- }
179
-
180
- /**
181
- * @expectedException \DiDom\Exceptions\InvalidSelectorException
182
- * @expectedExceptionMessage Invalid selector "."
183
- */
184
- public function testCompilehWithEmptyClassName()
185
- {
186
- Query::compile('span.');
187
- }
188
-
189
- public function testCompileXpath()
190
- {
191
- $this->assertEquals('//div', Query::compile('//div', Query::TYPE_XPATH));
192
- }
193
-
194
- public function testSetCompiledInvalidArgumentType()
195
- {
196
- if (PHP_VERSION_ID >= 70000) {
197
- $this->setExpectedException('TypeError');
198
- } else {
199
- $this->setExpectedException('PHPUnit_Framework_Error');
200
- }
201
-
202
- Query::setCompiled(null);
203
- }
204
-
205
- public function testSetCompiled()
206
- {
207
- $xpath = "//*[@id='foo']//*[contains(concat(' ', normalize-space(@class), ' '), ' bar ')]//baz";
208
- $compiled = ['#foo .bar baz' => $xpath];
209
-
210
- Query::setCompiled($compiled);
211
-
212
- $this->assertEquals($compiled, Query::getCompiled());
213
- }
214
-
215
- public function testGetCompiled()
216
- {
217
- Query::setCompiled([]);
218
-
219
- $selector = '#foo .bar baz';
220
- $xpath = '//*[@id="foo"]//*[contains(concat(" ", normalize-space(@class), " "), " bar ")]//baz';
221
- $compiled = [$selector => $xpath];
222
-
223
- Query::compile($selector);
224
-
225
- $this->assertEquals($compiled, Query::getCompiled());
226
- }
227
-
228
- public function compileCssTests()
229
- {
230
- $compiled = [
231
- ['a', '//a'],
232
- ['foo bar baz', '//foo//bar//baz'],
233
- ['foo > bar > baz', '//foo/bar/baz'],
234
- ['#foo', '//*[@id="foo"]'],
235
- ['.bar', '//*[contains(concat(" ", normalize-space(@class), " "), " bar ")]'],
236
- ['*[foo=bar]', '//*[@foo="bar"]'],
237
- ['*[foo="bar"]', '//*[@foo="bar"]'],
238
- ['*[foo=\'bar\']', '//*[@foo="bar"]'],
239
- ['select[name=category] option[selected=selected]', '//select[@name="category"]//option[@selected="selected"]'],
240
- ['*[^data-]', '//*[@*[starts-with(name(), "data-")]]'],
241
- ['*[^data-=foo]', '//*[@*[starts-with(name(), "data-")]="foo"]'],
242
- ['a[href^=https]', '//a[starts-with(@href, "https")]'],
243
- ['img[src$=png]', '//img[substring(@src, string-length(@src) - string-length("png") + 1) = "png"]'],
244
- ['a[href*=example.com]', '//a[contains(@href, "example.com")]'],
245
- ['script[!src]', '//script[not(@src)]'],
246
- ['a[href!="http://foo.com/"]', '//a[not(@href="http://foo.com/")]'],
247
- ['a[foo~="bar"]', '//a[contains(concat(" ", normalize-space(@foo), " "), " bar ")]'],
248
- ['input, textarea, select', '//input|//textarea|//select'],
249
- ['input[name="name"], textarea[name="description"], select[name="type"]', '//input[@name="name"]|//textarea[@name="description"]|//select[@name="type"]'],
250
- ['li:first-child', '//li[position() = 1]'],
251
- ['li:last-child', '//li[position() = last()]'],
252
- ['*:not(a[href*="example.com"])', '//*[not(self::a[contains(@href, "example.com")])]'],
253
- ['ul:empty', '//ul[count(descendant::*) = 0]'],
254
- ['ul:not-empty', '//ul[count(descendant::*) > 0]'],
255
- ['li:nth-child(odd)', '//*[(name()="li") and (position() mod 2 = 1 and position() >= 1)]'],
256
- ['li:nth-child(even)', '//*[(name()="li") and (position() mod 2 = 0 and position() >= 0)]'],
257
- ['li:nth-child(3)', '//*[(name()="li") and (position() = 3)]'],
258
- ['li:nth-child(-3)', '//*[(name()="li") and (position() = -3)]'],
259
- ['li:nth-child(3n)', '//*[(name()="li") and ((position() + 0) mod 3 = 0 and position() >= 0)]'],
260
- ['li:nth-child(3n+1)', '//*[(name()="li") and ((position() - 1) mod 3 = 0 and position() >= 1)]'],
261
- ['li:nth-child(3n-1)', '//*[(name()="li") and ((position() + 1) mod 3 = 0 and position() >= 1)]'],
262
- ['li:nth-child(n+3)', '//*[(name()="li") and ((position() - 3) mod 1 = 0 and position() >= 3)]'],
263
- ['li:nth-child(n-3)', '//*[(name()="li") and ((position() + 3) mod 1 = 0 and position() >= 3)]'],
264
- ['li:nth-of-type(odd)', '//li[position() mod 2 = 1 and position() >= 1]'],
265
- ['li:nth-of-type(even)', '//li[position() mod 2 = 0 and position() >= 0]'],
266
- ['li:nth-of-type(3)', '//li[position() = 3]'],
267
- ['li:nth-of-type(-3)', '//li[position() = -3]'],
268
- ['li:nth-of-type(3n)', '//li[(position() + 0) mod 3 = 0 and position() >= 0]'],
269
- ['li:nth-of-type(3n+1)', '//li[(position() - 1) mod 3 = 0 and position() >= 1]'],
270
- ['li:nth-of-type(3n-1)', '//li[(position() + 1) mod 3 = 0 and position() >= 1]'],
271
- ['li:nth-of-type(n+3)', '//li[(position() - 3) mod 1 = 0 and position() >= 3]'],
272
- ['li:nth-of-type(n-3)', '//li[(position() + 3) mod 1 = 0 and position() >= 3]'],
273
- ['ul:has(li.item)', '//ul[.//li[contains(concat(" ", normalize-space(@class), " "), " item ")]]'],
274
- ['form[name=register]:has(input[name=foo])', '//form[(@name="register") and (.//input[@name="foo"])]'],
275
- ['ul li a::text', '//ul//li//a/text()'],
276
- ['ul li a::text()', '//ul//li//a/text()'],
277
- ['ul li a::attr(href)', '//ul//li//a/@*[name() = "href"]'],
278
- ['ul li a::attr(href, title)', '//ul//li//a/@*[name() = "href" or name() = "title"]'],
279
- ['> ul li a', '/ul//li//a'],
280
- ];
281
-
282
- $compiled = array_merge($compiled, $this->getContainsPseudoClassTests());
283
- $compiled = array_merge($compiled, $this->getPropertiesTests());
284
-
285
- $compiled = array_merge($compiled, [
286
- ['a[title="foo, bar::baz"]', '//a[@title="foo, bar::baz"]'],
287
- ]);
288
-
289
- return $compiled;
290
- }
291
-
292
- private function getContainsPseudoClassTests()
293
- {
294
- $strToLowerFunction = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
295
-
296
- $containsXpath = [
297
- // caseSensitive = true, fullMatch = false
298
- ['li:contains(foo)', '//li[contains(text(), "foo")]'],
299
- ['li:contains("foo")', '//li[contains(text(), "foo")]'],
300
- ['li:contains(\'foo\')', '//li[contains(text(), "foo")]'],
301
-
302
- // caseSensitive = true, fullMatch = false
303
- ['li:contains(foo, true)', '//li[contains(text(), "foo")]'],
304
- ['li:contains("foo", true)', '//li[contains(text(), "foo")]'],
305
- ['li:contains(\'foo\', true)', '//li[contains(text(), "foo")]'],
306
-
307
- // caseSensitive = true, fullMatch = false
308
- ['li:contains(foo, true, false)', '//li[contains(text(), "foo")]'],
309
- ['li:contains("foo", true, false)', '//li[contains(text(), "foo")]'],
310
- ['li:contains(\'foo\', true, false)', '//li[contains(text(), "foo")]'],
311
-
312
- // caseSensitive = true, fullMatch = true
313
- ['li:contains(foo, true, true)', '//li[text() = "foo"]'],
314
- ['li:contains("foo", true, true)', '//li[text() = "foo"]'],
315
- ['li:contains(\'foo\', true, true)', '//li[text() = "foo"]'],
316
-
317
- // caseSensitive = false, fullMatch = false
318
- ['li:contains(foo, false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
319
- ['li:contains("foo", false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
320
- ['li:contains(\'foo\', false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
321
-
322
- // caseSensitive = false, fullMatch = false
323
- ['li:contains(foo, false, false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
324
- ['li:contains("foo", false, false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
325
- ['li:contains(\'foo\', false, false)', "//li[contains(php:functionString(\"{$strToLowerFunction}\", .), php:functionString(\"{$strToLowerFunction}\", \"foo\"))]"],
326
-
327
- // caseSensitive = false, fullMatch = true
328
- ['li:contains(foo, false, true)', "//li[php:functionString(\"{$strToLowerFunction}\", .) = php:functionString(\"{$strToLowerFunction}\", \"foo\")]"],
329
- ['li:contains("foo", false, true)', "//li[php:functionString(\"{$strToLowerFunction}\", .) = php:functionString(\"{$strToLowerFunction}\", \"foo\")]"],
330
- ['li:contains(\'foo\', false, true)', "//li[php:functionString(\"{$strToLowerFunction}\", .) = php:functionString(\"{$strToLowerFunction}\", \"foo\")]"],
331
- ];
332
-
333
- return $containsXpath;
334
- }
335
-
336
- private function getPropertiesTests()
337
- {
338
- return [
339
- ['a::text', '//a/text()'],
340
- ['a::text()', '//a/text()'],
341
- ['a::attr', '//a/@*'],
342
- ['a::attr()', '//a/@*'],
343
- ['a::attr(href)', '//a/@*[name() = "href"]'],
344
- ['a::attr(href,title)', '//a/@*[name() = "href" or name() = "title"]'],
345
- ['a::attr(href, title)', '//a/@*[name() = "href" or name() = "title"]'],
346
- ];
347
- }
348
-
349
- public function buildXpathTests()
350
- {
351
- $xpath = [
352
- '//a',
353
- '//*[@id="foo"]',
354
- '//a[@id="foo"]',
355
- '//a[contains(concat(" ", normalize-space(@class), " "), " foo ")]',
356
- '//a[(contains(concat(" ", normalize-space(@class), " "), " foo ")) and (contains(concat(" ", normalize-space(@class), " "), " bar "))]',
357
- '//a[@href]',
358
- '//a[@href="http://example.com/"]',
359
- '//a[(@href="http://example.com/") and (@title="Example Domain")]',
360
- '//a[(@target="_blank") and (starts-with(@href, "https"))]',
361
- '//a[substring(@href, string-length(@href) - string-length(".com") + 1) = ".com"]',
362
- '//a[contains(@href, "example")]',
363
- '//a[not(@href="http://foo.com/")]',
364
- '//script[not(@src)]',
365
- '//li[position() = 1]',
366
- '//*[(@id="id") and (contains(concat(" ", normalize-space(@class), " "), " foo ")) and (@name="value") and (position() = 1)]',
367
- ];
368
-
369
- $segments = [
370
- ['tag' => 'a'],
371
- ['id' => 'foo'],
372
- ['tag' => 'a', 'id' => 'foo'],
373
- ['tag' => 'a', 'classes' => ['foo']],
374
- ['tag' => 'a', 'classes' => ['foo', 'bar']],
375
- ['tag' => 'a', 'attributes' => ['href' => null]],
376
- ['tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
377
- ['tag' => 'a', 'attributes' => ['href' => 'http://example.com/', 'title' => 'Example Domain']],
378
- ['tag' => 'a', 'attributes' => ['target' => '_blank', 'href^' => 'https']],
379
- ['tag' => 'a', 'attributes' => ['href$' => '.com']],
380
- ['tag' => 'a', 'attributes' => ['href*' => 'example']],
381
- ['tag' => 'a', 'attributes' => ['href!' => 'http://foo.com/']],
382
- ['tag' => 'script', 'attributes' => ['!src' => null]],
383
- ['tag' => 'li', 'pseudo' => 'first-child'],
384
- ['tag' => '*', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
385
- ];
386
-
387
- $parameters = [];
388
-
389
- foreach ($segments as $index => $segment) {
390
- $parameters[] = [$segment, $xpath[$index]];
391
- }
392
-
393
- return $parameters;
394
- }
395
-
396
- public function getSegmentsTests()
397
- {
398
- $segments = [
399
- ['selector' => 'a', 'tag' => 'a'],
400
- ['selector' => '#foo', 'id' => 'foo'],
401
- ['selector' => 'a#foo', 'tag' => 'a', 'id' => 'foo'],
402
- ['selector' => 'a.foo', 'tag' => 'a', 'classes' => ['foo']],
403
- ['selector' => 'a.foo.bar', 'tag' => 'a', 'classes' => ['foo', 'bar']],
404
- ['selector' => 'a[href]', 'tag' => 'a', 'attributes' => ['href' => null]],
405
- ['selector' => 'a[href=http://example.com/]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
406
- ['selector' => 'a[href="http://example.com/"]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
407
- ['selector' => 'a[href=\'http://example.com/\']', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
408
- ['selector' => 'a[href=http://example.com/][title=Example Domain]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/', 'title' => 'Example Domain']],
409
- ['selector' => 'a[href=http://example.com/][href=http://example.com/404]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/404']],
410
- ['selector' => 'a[href^=https]', 'tag' => 'a', 'attributes' => ['href^' => 'https']],
411
- ['selector' => 'li:first-child', 'tag' => 'li', 'pseudo' => 'first-child'],
412
- ['selector' => 'ul >', 'tag' => 'ul', 'rel' => '>'],
413
- ['selector' => '#id.foo[name=value]:first-child >', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
414
- ['selector' => 'li.bar:nth-child(2n)', 'tag' => 'li', 'classes' => ['bar'], 'pseudo' => 'nth-child', 'expr' => '2n'],
415
- ];
416
-
417
- $parameters = [];
418
-
419
- foreach ($segments as $segment) {
420
- $parameters[] = [$segment['selector'], $segment];
421
- }
422
-
423
- return $parameters;
424
- }
425
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/DiDom/SelectorTest.php DELETED
@@ -1,327 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Document;
6
- use Tests\TestCase;
7
-
8
- class SelectorTest extends TestCase
9
- {
10
- public function testTag()
11
- {
12
- $html = '
13
- <ul id="first">
14
- <li><a href="#">Item 1</a></li>
15
- <li><a href="#">Item 2</a></li>
16
- <li><a href="#">Item 3</a></li>
17
- </ul>
18
- <ol id="second">
19
- <li><a href="#">Item 1</a></li>
20
- <li><a href="#">Item 2</a></li>
21
- <li><a href="#">Item 3</a></li>
22
- </ol>
23
- ';
24
-
25
- $document = new Document($html);
26
-
27
- $expected = ['Item 1', 'Item 2', 'Item 3', 'Item 1', 'Item 2', 'Item 3'];
28
-
29
- $result = [];
30
-
31
- foreach ($document->find('li') as $element) {
32
- $result[] = $element->text();
33
- }
34
-
35
- $this->assertEquals($expected, $result);
36
- }
37
-
38
- public function testNestedTag()
39
- {
40
- $html = '
41
- <ul id="first">
42
- <li><a href="#">Item 1</a></li>
43
- <li><a href="#">Item 2</a></li>
44
- <li><a href="#">Item 3</a></li>
45
- </ul>
46
- <ol id="second">
47
- <li><a href="#">Item 1</a></li>
48
- <li><a href="#">Item 2</a></li>
49
- <li><a href="#">Item 3</a></li>
50
- </ol>
51
- ';
52
-
53
- $document = new Document($html);
54
-
55
- $expected = ['Item 1', 'Item 2', 'Item 3'];
56
-
57
- $result = [];
58
-
59
- foreach ($document->find('ul a') as $element) {
60
- $result[] = $element->text();
61
- }
62
-
63
- $this->assertEquals($expected, $result);
64
- }
65
-
66
- public function testDirectChild()
67
- {
68
- $html = '
69
- <div>
70
- <p><span>Lorem ipsum.</span></p>
71
- <span>Lorem ipsum.</span>
72
- </div>
73
- ';
74
-
75
- $document = new Document($html);
76
-
77
- $expected = ['Lorem ipsum.'];
78
-
79
- $result = [];
80
-
81
- foreach ($document->find('div > span') as $element) {
82
- $result[] = $element->text();
83
- }
84
-
85
- $this->assertEquals($expected, $result);
86
- }
87
-
88
- public function testId()
89
- {
90
- $html = '
91
- <span>Lorem ipsum dolor.</span>
92
- <span id="second">Tenetur totam, nostrum.</span>
93
- <span>Iste, doloremque, praesentium.</span>
94
- ';
95
-
96
- $document = new Document($html);
97
-
98
- $expected = ['Tenetur totam, nostrum.'];
99
-
100
- $result = [];
101
-
102
- foreach ($document->find('#second') as $element) {
103
- $result[] = $element->text();
104
- }
105
-
106
- $this->assertEquals($expected, $result);
107
- }
108
-
109
- public function testClass()
110
- {
111
- $html = '
112
- <span class="odd first">Lorem ipsum dolor.</span>
113
- <span class="even second">Tenetur totam, nostrum.</span>
114
- <span class="odd third">Iste, doloremque, praesentium.</span>
115
- ';
116
-
117
- $document = new Document($html);
118
-
119
- $expected = ['Lorem ipsum dolor.', 'Iste, doloremque, praesentium.'];
120
-
121
- $result = [];
122
-
123
- foreach ($document->find('.odd') as $element) {
124
- $result[] = $element->text();
125
- }
126
-
127
- $this->assertEquals($expected, $result);
128
-
129
- $expected = ['Iste, doloremque, praesentium.'];
130
-
131
- $result = [];
132
-
133
- foreach ($document->find('.odd.third') as $element) {
134
- $result[] = $element->text();
135
- }
136
-
137
- $this->assertEquals($expected, $result);
138
- }
139
-
140
- public function testAttributes()
141
- {
142
- $html = '
143
- <ul class="links">
144
- <li>
145
- <a href="https://foo.com" title="Foo" target="_blank">Foo</a>
146
- <a href="http://bar.com" title="Bar" rel="noreferrer">Bar</a>
147
- <a href="https://baz.org" title="Baz" rel="nofollow noreferrer">Baz</a>
148
- <a href="http://qux.org" title="Qux" target="_blank" rel="nofollow">Qux</a>
149
- </li>
150
- </ul>
151
- ';
152
-
153
- $document = new Document($html);
154
-
155
- // has attribute
156
-
157
- $expected = ['Foo', 'Qux'];
158
-
159
- $result = [];
160
-
161
- foreach ($document->find('a[target]') as $element) {
162
- $result[] = $element->text();
163
- }
164
-
165
- $this->assertEquals($expected, $result);
166
-
167
- // has no attribute
168
-
169
- $expected = ['Bar', 'Baz'];
170
-
171
- $result = [];
172
-
173
- foreach ($document->find('a[!target]') as $element) {
174
- $result[] = $element->text();
175
- }
176
-
177
- $this->assertEquals($expected, $result);
178
-
179
- // equals
180
-
181
- $expected = ['Baz'];
182
-
183
- $result = [];
184
-
185
- foreach ($document->find('a[href="https://baz.org"]') as $element) {
186
- $result[] = $element->text();
187
- }
188
-
189
- $this->assertEquals($expected, $result);
190
-
191
- // not equals
192
-
193
- $expected = ['Foo', 'Bar', 'Qux'];
194
-
195
- $result = [];
196
-
197
- foreach ($document->find('a[href!="https://baz.org"]') as $element) {
198
- $result[] = $element->text();
199
- }
200
-
201
- $this->assertEquals($expected, $result);
202
-
203
- // starts with
204
-
205
- $expected = ['Foo', 'Baz'];
206
-
207
- $result = [];
208
-
209
- foreach ($document->find('a[href^="https"]') as $element) {
210
- $result[] = $element->text();
211
- }
212
-
213
- $this->assertEquals($expected, $result);
214
-
215
- // ends with
216
-
217
- $expected = ['Baz', 'Qux'];
218
-
219
- $result = [];
220
-
221
- foreach ($document->find('a[href$="org"]') as $element) {
222
- $result[] = $element->text();
223
- }
224
-
225
- $this->assertEquals($expected, $result);
226
-
227
- // contains word
228
-
229
- $expected = ['Bar', 'Baz'];
230
-
231
- $result = [];
232
-
233
- foreach ($document->find('a[rel~="noreferrer"]') as $element) {
234
- $result[] = $element->text();
235
- }
236
-
237
- $this->assertEquals($expected, $result);
238
- $this->assertEquals([], $document->find('a[rel~="noref"]'));
239
-
240
- // contains substring
241
-
242
- $expected = ['Bar', 'Baz'];
243
-
244
- $result = [];
245
-
246
- foreach ($document->find('a[href*="ba"]') as $element) {
247
- $result[] = $element->text();
248
- }
249
-
250
- $this->assertEquals($expected, $result);
251
-
252
- // multiple attribute conditions
253
-
254
- $expected = ['Qux'];
255
-
256
- $result = [];
257
-
258
- foreach ($document->find('a[target="_blank"][rel="nofollow"]') as $element) {
259
- $result[] = $element->text();
260
- }
261
-
262
- $this->assertEquals($expected, $result);
263
- }
264
-
265
- /**
266
- * @param $selector
267
- * @param $expectedResult
268
- *
269
- * @dataProvider containsPseudoClassTests
270
- */
271
- public function testContainsPseudoClass($selector, $expectedResult)
272
- {
273
- $html = '
274
- <ul class="links">
275
- <li>
276
- <a href="https://foo.com" title="Foo" target="_blank">Foo</a>
277
- <a href="http://bar.com" title="Bar" rel="noreferrer">Bar</a>
278
- <a href="https://baz.org" title="Baz" rel="nofollow noreferrer">Baz</a>
279
- <a href="http://qux.org" title="Qux" target="_blank" rel="nofollow">Qux</a>
280
- <a href="https://foobar.com" title="FooBar" target="_blank">FooBar</a>
281
- </li>
282
- </ul>
283
- ';
284
-
285
- $document = new Document($html);
286
-
287
- $result = [];
288
-
289
- foreach ($document->find($selector) as $element) {
290
- $result[] = $element->text();
291
- }
292
-
293
- $this->assertEquals($expectedResult, $result);
294
- }
295
-
296
- public function containsPseudoClassTests()
297
- {
298
- return [
299
- ['a:contains(Baz)', ['Baz']],
300
- ['a:contains(a)', ['Bar', 'Baz', 'FooBar']],
301
- ['a:contains(Bar)', ['Bar', 'FooBar']],
302
- ['a:contains(Bar, true, true)', ['Bar']],
303
- ['a:contains(bar)', []],
304
- ['a:contains(bar, false)', ['Bar', 'FooBar']],
305
- ['a:contains(bar, false, true)', ['Bar']],
306
- ];
307
- }
308
-
309
- public function testUnicodeSupport()
310
- {
311
- $html = '
312
- <ul class="links">
313
- <li>
314
- <a href="http://foo.com" title="Foo">Foo</a>
315
- <a href="http://example.com" title="Пример">Example</a>
316
- <a href="http://bar.com" title="Foo">Bar</a>
317
- <a href="http://example.ru" title="Example">Пример</a>
318
- </li>
319
- </ul>
320
- ';
321
-
322
- $document = new Document($html);
323
-
324
- $this->assertEquals('Example', $document->first('a[title=Пример]')->text());
325
- $this->assertEquals('Example', $document->first('a:contains(Пример)')->attr('title'));
326
- }
327
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/DiDom/StyleAttributeTest.php DELETED
@@ -1,515 +0,0 @@
1
- <?php
2
-
3
- namespace Tests\DiDom;
4
-
5
- use DiDom\Element;
6
- use DiDom\StyleAttribute;
7
- use Tests\TestCase;
8
-
9
- class StyleAttributeTest extends TestCase
10
- {
11
- /**
12
- * @expectedException \InvalidArgumentException
13
- * @expectedExceptionMessage The element must contain DOMElement node
14
- */
15
- public function testConstructorWithTextNode()
16
- {
17
- $element = new Element(new \DOMText('foo'));
18
-
19
- new StyleAttribute($element);
20
- }
21
-
22
- /**
23
- * @expectedException \InvalidArgumentException
24
- * @expectedExceptionMessage The element must contain DOMElement node
25
- */
26
- public function testConstructorWithCommentNode()
27
- {
28
- $element = new Element(new \DOMComment('foo'));
29
-
30
- new StyleAttribute($element);
31
- }
32
-
33
- /**
34
- * @expectedException \InvalidArgumentException
35
- * @expectedExceptionMessage DiDom\StyleAttribute::setProperty expects parameter 1 to be string, NULL given
36
- */
37
- public function testSetPropertyWithInvalidPropertyName()
38
- {
39
- $element = new Element('div', null, [
40
- 'style' => 'color: blue; border: 1px solid black',
41
- ]);
42
-
43
- $styleAttribute = new StyleAttribute($element);
44
-
45
- $styleAttribute->setProperty(null, '16px');
46
- }
47
-
48
- /**
49
- * @expectedException \InvalidArgumentException
50
- * @expectedExceptionMessage DiDom\StyleAttribute::setProperty expects parameter 2 to be string, NULL given
51
- */
52
- public function testSetPropertyWithInvalidPropertyValue()
53
- {
54
- $element = new Element('div', null, [
55
- 'style' => 'color: blue; border: 1px solid black',
56
- ]);
57
-
58
- $styleAttribute = new StyleAttribute($element);
59
-
60
- $styleAttribute->setProperty('font-size', null);
61
- }
62
-
63
- public function testSetProperty()
64
- {
65
- $element = new Element('div', null, [
66
- 'style' => 'color: blue; border: 1px solid black',
67
- ]);
68
-
69
- $styleAttribute = new StyleAttribute($element);
70
-
71
- $this->assertEquals('color: blue; border: 1px solid black', $element->getAttribute('style'));
72
-
73
- $styleAttribute->setProperty('font-size', '16px');
74
-
75
- $this->assertEquals('color: blue; border: 1px solid black; font-size: 16px', $element->getAttribute('style'));
76
- }
77
-
78
- /**
79
- * @expectedException \InvalidArgumentException
80
- * @expectedExceptionMessage Property name must be a string, integer given
81
- */
82
- public function testSetMultiplePropertiesWithInvalidPropertyName()
83
- {
84
- $element = new Element('div', null, [
85
- 'style' => 'color: blue; border: 1px solid black',
86
- ]);
87
-
88
- $styleAttribute = new StyleAttribute($element);
89
-
90
- $styleAttribute->setMultipleProperties([
91
- 'width' => '50px',
92
- 'height',
93
- ]);
94
- }
95
-
96
- /**
97
- * @expectedException \InvalidArgumentException
98
- * @expectedExceptionMessage Property value must be a string, NULL given
99
- */
100
- public function testSetMultiplePropertiesWithInvalidPropertyValue()
101
- {
102
- $element = new Element('div', null, [
103
- 'style' => 'color: blue; border: 1px solid black',
104
- ]);
105
-
106
- $styleAttribute = new StyleAttribute($element);
107
-
108
- $styleAttribute->setMultipleProperties([
109
- 'width' => '50px',
110
- 'height' => null,
111
- ]);
112
- }
113
-
114
- public function testSetMultipleProperties()
115
- {
116
- $element = new Element('div', null, [
117
- 'style' => 'color: blue; border: 1px solid black',
118
- ]);
119
-
120
- $styleAttribute = new StyleAttribute($element);
121
-
122
- $this->assertEquals('color: blue; border: 1px solid black', $element->getAttribute('style'));
123
-
124
- $styleAttribute->setMultipleProperties([
125
- 'font-size' => '16px',
126
- 'font-family' => 'Times',
127
- ]);
128
-
129
- $this->assertEquals('color: blue; border: 1px solid black; font-size: 16px; font-family: Times', $element->getAttribute('style'));
130
- }
131
-
132
- /**
133
- * @expectedException \InvalidArgumentException
134
- * @expectedExceptionMessage DiDom\StyleAttribute::getProperty expects parameter 1 to be string, NULL given
135
- */
136
- public function testGetPropertyWithInvalidPropertyName()
137
- {
138
- $element = new Element('div', null, [
139
- 'style' => 'color: blue; border: 1px solid black',
140
- ]);
141
-
142
- $styleAttribute = new StyleAttribute($element);
143
-
144
- $styleAttribute->getProperty(null);
145
- }
146
-
147
- /**
148
- * @param string $styleString
149
- * @param string $propertyName
150
- * @param string $expectedResult
151
- *
152
- * @dataProvider getPropertyDataProvider
153
- */
154
- public function testGetProperty($styleString, $propertyName, $expectedResult)
155
- {
156
- $element = new Element('div', null, [
157
- 'style' => $styleString,
158
- ]);
159
-
160
- $styleAttribute = new StyleAttribute($element);
161
-
162
- $this->assertEquals($expectedResult, $styleAttribute->getProperty($propertyName));
163
- }
164
-
165
- public function getPropertyDataProvider()
166
- {
167
- return [
168
- [
169
- 'color: blue; font-size: 16px; border: 1px solid black',
170
- 'font-size',
171
- '16px',
172
- ],
173
- [
174
- 'color: blue; font-size: 16px; border: 1px solid black;',
175
- 'font-size',
176
- '16px',
177
- ],
178
- [
179
- 'color: blue; font-size: 16px; border: 1px solid black;',
180
- 'foo',
181
- null,
182
- ],
183
- ];
184
- }
185
-
186
- public function testGetPropertyWithDefaultValue()
187
- {
188
- $element = new Element('div', null, [
189
- 'style' => 'color: blue',
190
- ]);
191
-
192
- $styleAttribute = new StyleAttribute($element);
193
-
194
- $this->assertNull($styleAttribute->getProperty('font-size'));
195
- $this->assertEquals('16px', $styleAttribute->getProperty('font-size', '16px'));
196
- }
197
-
198
- /**
199
- * @expectedException \InvalidArgumentException
200
- * @expectedExceptionMessage Property name must be a string, NULL given
201
- */
202
- public function testGetMultiplePropertiesWithInvalidPropertyName()
203
- {
204
- $element = new Element('div', null, [
205
- 'style' => 'color: blue; border: 1px solid black',
206
- ]);
207
-
208
- $styleAttribute = new StyleAttribute($element);
209
-
210
- $styleAttribute->getMultipleProperties(['color', null]);
211
- }
212
-
213
- /**
214
- * @param string $styleString
215
- * @param array $propertyNames
216
- * @param string $expectedResult
217
- *
218
- * @dataProvider getMultiplePropertiesDataProvider
219
- */
220
- public function testGetMultipleProperties($styleString, $propertyNames, $expectedResult)
221
- {
222
- $element = new Element('div', null, [
223
- 'style' => $styleString,
224
- ]);
225
-
226
- $styleAttribute = new StyleAttribute($element);
227
-
228
- $this->assertEquals($expectedResult, $styleAttribute->getMultipleProperties($propertyNames));
229
- }
230
-
231
- public function getMultiplePropertiesDataProvider()
232
- {
233
- return [
234
- [
235
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
236
- ['font-size'],
237
- [
238
- 'font-size' => '16px',
239
- ],
240
- ],
241
- [
242
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
243
- ['font-size', 'border'],
244
- [
245
- 'font-size' => '16px',
246
- 'border' => '1px solid black',
247
- ],
248
- ],
249
- [
250
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
251
- ['font-size', 'border', 'width'],
252
- [
253
- 'font-size' => '16px',
254
- 'border' => '1px solid black',
255
- ],
256
- ],
257
- ];
258
- }
259
-
260
- /**
261
- * @param string $styleString
262
- * @param string $expectedResult
263
- *
264
- * @dataProvider getAllPropertiesDataProvider
265
- */
266
- public function testGetAllProperties($styleString, $expectedResult)
267
- {
268
- $element = new Element('div', null, [
269
- 'style' => $styleString,
270
- ]);
271
-
272
- $styleAttribute = new StyleAttribute($element);
273
-
274
- $this->assertEquals($expectedResult, $styleAttribute->getAllProperties());
275
- }
276
-
277
- public function getAllPropertiesDataProvider()
278
- {
279
- return [
280
- [
281
- '',
282
- [],
283
- ],
284
- [
285
- 'color: blue; font-size: 16px; border: 1px solid black',
286
- [
287
- 'color' => 'blue',
288
- 'font-size' => '16px',
289
- 'border' => '1px solid black',
290
- ],
291
- ],
292
- [
293
- 'color: blue; font-size: 16px; border: 1px solid black',
294
- [
295
- 'color' => 'blue',
296
- 'font-size' => '16px',
297
- 'border' => '1px solid black',
298
- ],
299
- ],
300
- ];
301
- }
302
-
303
- public function testGetAllPropertiesAfterEmptyStyleAttribute()
304
- {
305
- $element = new Element('div', null, [
306
- 'style' => 'color: blue',
307
- ]);
308
-
309
- $styleAttribute = new StyleAttribute($element);
310
-
311
- $this->assertEquals(['color' => 'blue'], $styleAttribute->getAllProperties());
312
-
313
- $element->setAttribute('style', '');
314
-
315
- $this->assertEquals([], $styleAttribute->getAllProperties());
316
- }
317
-
318
- /**
319
- * @expectedException \InvalidArgumentException
320
- * @expectedExceptionMessage DiDom\StyleAttribute::hasProperty expects parameter 1 to be string, NULL given
321
- */
322
- public function testHasPropertyWithInvalidPropertyName()
323
- {
324
- $element = new Element('div', null, [
325
- 'style' => 'color: blue; border: 1px solid black',
326
- ]);
327
-
328
- $styleAttribute = new StyleAttribute($element);
329
-
330
- $styleAttribute->hasProperty(null);
331
- }
332
-
333
- public function testHasProperty()
334
- {
335
- $element = new Element('div', null, [
336
- 'style' => 'color: blue; border: 1px solid black',
337
- ]);
338
-
339
- $styleAttribute = new StyleAttribute($element);
340
-
341
- $this->assertTrue($styleAttribute->hasProperty('color'));
342
- $this->assertFalse($styleAttribute->hasProperty('width'));
343
- }
344
-
345
- /**
346
- * @expectedException \InvalidArgumentException
347
- * @expectedExceptionMessage DiDom\StyleAttribute::removeProperty expects parameter 1 to be string, NULL given
348
- */
349
- public function testRemovePropertyWithInvalidPropertyName()
350
- {
351
- $element = new Element('div', null, [
352
- 'style' => 'color: blue; border: 1px solid black',
353
- ]);
354
-
355
- $styleAttribute = new StyleAttribute($element);
356
-
357
- $styleAttribute->removeProperty(null);
358
- }
359
-
360
- public function testRemoveProperty()
361
- {
362
- $styleString = 'color: blue; font-size: 16px; border: 1px solid black';
363
-
364
- $element = new Element('span', 'foo', [
365
- 'style' => $styleString,
366
- ]);
367
-
368
- $styleAttribute = new StyleAttribute($element);
369
-
370
- $this->assertEquals($styleString, $element->getAttribute('style'));
371
-
372
- $styleAttribute->removeProperty('font-size');
373
-
374
- $this->assertEquals('color: blue; border: 1px solid black', $element->getAttribute('style'));
375
- }
376
-
377
- /**
378
- * @expectedException \InvalidArgumentException
379
- * @expectedExceptionMessage Property name must be a string, NULL given
380
- */
381
- public function testRemoveMultiplePropertiesWithInvalidPropertyName()
382
- {
383
- $element = new Element('div', null, [
384
- 'style' => 'color: blue; border: 1px solid black',
385
- ]);
386
-
387
- $styleAttribute = new StyleAttribute($element);
388
-
389
- $styleAttribute->removeMultipleProperties(['color', null]);
390
- }
391
-
392
- /**
393
- * @param string $styleString
394
- * @param array $propertyNames
395
- * @param string $expectedResult
396
- *
397
- * @dataProvider removeMultiplePropertiesDataProvider
398
- */
399
- public function testRemoveMultipleProperties($styleString, $propertyNames, $expectedResult)
400
- {
401
- $element = new Element('div', null, [
402
- 'style' => $styleString,
403
- ]);
404
-
405
- $styleAttribute = new StyleAttribute($element);
406
-
407
- $this->assertEquals($styleString, $element->getAttribute('style'));
408
-
409
- $styleAttribute->removeMultipleProperties($propertyNames);
410
-
411
- $this->assertEquals($expectedResult, $element->getAttribute('style'));
412
- }
413
-
414
- public function removeMultiplePropertiesDataProvider()
415
- {
416
- return [
417
- [
418
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
419
- [
420
- 'font-size',
421
- ],
422
- 'color: blue; font-family: Times; border: 1px solid black',
423
- ],
424
- [
425
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
426
- [
427
- 'font-size', 'border',
428
- ],
429
- 'color: blue; font-family: Times',
430
- ],
431
- [
432
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
433
- [
434
- 'font-size', 'border', 'width',
435
- ],
436
- 'color: blue; font-family: Times',
437
- ],
438
- ];
439
- }
440
-
441
- /**
442
- * @expectedException \InvalidArgumentException
443
- * @expectedExceptionMessage Property name must be a string, NULL given
444
- */
445
- public function testRemoveAllPropertiesWithInvalidPropertyName()
446
- {
447
- $element = new Element('div', null, [
448
- 'style' => 'color: blue; border: 1px solid black',
449
- ]);
450
-
451
- $styleAttribute = new StyleAttribute($element);
452
-
453
- $styleAttribute->removeAllProperties(['color', null]);
454
- }
455
-
456
- /**
457
- * @param string $styleString
458
- * @param array $exclusions
459
- * @param string $expectedResult
460
- *
461
- * @dataProvider removeAllPropertiesDataProvider
462
- */
463
- public function testRemoveAllProperties($styleString, $exclusions, $expectedResult)
464
- {
465
- $element = new Element('div', null, [
466
- 'style' => $styleString,
467
- ]);
468
-
469
- $styleAttribute = new StyleAttribute($element);
470
-
471
- $this->assertEquals($styleString, $element->getAttribute('style'));
472
-
473
- $styleAttribute->removeAllProperties($exclusions);
474
-
475
- $this->assertEquals($expectedResult, $element->getAttribute('style'));
476
- }
477
-
478
- public function removeAllPropertiesDataProvider()
479
- {
480
- return [
481
- [
482
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
483
- [
484
- 'font-size',
485
- ],
486
- 'font-size: 16px',
487
- ],
488
- [
489
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
490
- [
491
- 'font-size', 'border',
492
- ],
493
- 'font-size: 16px; border: 1px solid black',
494
- ],
495
- [
496
- 'color: blue; font-size: 16px; font-family: Times; border: 1px solid black',
497
- [
498
- 'font-size', 'border', 'width',
499
- ],
500
- 'font-size: 16px; border: 1px solid black',
501
- ],
502
- ];
503
- }
504
-
505
- public function testGetElement()
506
- {
507
- $element = new Element('div', null, [
508
- 'style' => 'color: blue; font-size: 16px',
509
- ]);
510
-
511
- $styleAttribute = new StyleAttribute($element);
512
-
513
- $this->assertSame($element, $styleAttribute->getElement());
514
- }
515
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/TestCase.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
-
3
- namespace Tests;
4
-
5
- use PHPUnit_Framework_TestCase;
6
- use DOMDocument;
7
- use Exception;
8
-
9
- class TestCase extends PHPUnit_Framework_TestCase
10
- {
11
- protected function tearDown()
12
- {
13
- if (class_exists('Mockery')) {
14
- \Mockery::close();
15
- }
16
- }
17
-
18
- protected function loadFixture($filename)
19
- {
20
- $path = __DIR__.'/fixtures/'.$filename;
21
-
22
- if (file_exists($path)) {
23
- return file_get_contents($path);
24
- }
25
-
26
- throw new Exception(sprintf('Fixture "%s" does not exist', $filename));
27
- }
28
-
29
- protected function createDomElement($name, $value = null, $attributes = [])
30
- {
31
- $document = new DOMDocument('1.0', 'UTF-8');
32
- $node = $document->createElement($name, $value);
33
-
34
- foreach ($attributes as $name => $value) {
35
- $node->setAttribute($name, $value);
36
- }
37
-
38
- return $node;
39
- }
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/bootstrap.php DELETED
@@ -1,7 +0,0 @@
1
- <?php
2
-
3
- ini_set('error_reporting', E_ALL);
4
- ini_set('display_errors', 1);
5
- ini_set('display_startup_errors', 1);
6
-
7
- require __DIR__.'/../vendor/autoload.php';
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/fixtures/books.xml DELETED
@@ -1,120 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <catalog>
3
- <book id="bk101">
4
- <author>Gambardella, Matthew</author>
5
- <title>XML Developer's Guide</title>
6
- <genre>Computer</genre>
7
- <price>44.95</price>
8
- <publish_date>2000-10-01</publish_date>
9
- <description>An in-depth look at creating applications
10
- with XML.</description>
11
- </book>
12
- <book id="bk102">
13
- <author>Ralls, Kim</author>
14
- <title>Midnight Rain</title>
15
- <genre>Fantasy</genre>
16
- <price>5.95</price>
17
- <publish_date>2000-12-16</publish_date>
18
- <description>A former architect battles corporate zombies,
19
- an evil sorceress, and her own childhood to become queen
20
- of the world.</description>
21
- </book>
22
- <book id="bk103">
23
- <author>Corets, Eva</author>
24
- <title>Maeve Ascendant</title>
25
- <genre>Fantasy</genre>
26
- <price>5.95</price>
27
- <publish_date>2000-11-17</publish_date>
28
- <description>After the collapse of a nanotechnology
29
- society in England, the young survivors lay the
30
- foundation for a new society.</description>
31
- </book>
32
- <book id="bk104">
33
- <author>Corets, Eva</author>
34
- <title>Oberon's Legacy</title>
35
- <genre>Fantasy</genre>
36
- <price>5.95</price>
37
- <publish_date>2001-03-10</publish_date>
38
- <description>In post-apocalypse England, the mysterious
39
- agent known only as Oberon helps to create a new life
40
- for the inhabitants of London. Sequel to Maeve
41
- Ascendant.</description>
42
- </book>
43
- <book id="bk105">
44
- <author>Corets, Eva</author>
45
- <title>The Sundered Grail</title>
46
- <genre>Fantasy</genre>
47
- <price>5.95</price>
48
- <publish_date>2001-09-10</publish_date>
49
- <description>The two daughters of Maeve, half-sisters,
50
- battle one another for control of England. Sequel to
51
- Oberon's Legacy.</description>
52
- </book>
53
- <book id="bk106">
54
- <author>Randall, Cynthia</author>
55
- <title>Lover Birds</title>
56
- <genre>Romance</genre>
57
- <price>4.95</price>
58
- <publish_date>2000-09-02</publish_date>
59
- <description>When Carla meets Paul at an ornithology
60
- conference, tempers fly as feathers get ruffled.</description>
61
- </book>
62
- <book id="bk107">
63
- <author>Thurman, Paula</author>
64
- <title>Splish Splash</title>
65
- <genre>Romance</genre>
66
- <price>4.95</price>
67
- <publish_date>2000-11-02</publish_date>
68
- <description>A deep sea diver finds true love twenty
69
- thousand leagues beneath the sea.</description>
70
- </book>
71
- <book id="bk108">
72
- <author>Knorr, Stefan</author>
73
- <title>Creepy Crawlies</title>
74
- <genre>Horror</genre>
75
- <price>4.95</price>
76
- <publish_date>2000-12-06</publish_date>
77
- <description>An anthology of horror stories about roaches,
78
- centipedes, scorpions and other insects.</description>
79
- </book>
80
- <book id="bk109">
81
- <author>Kress, Peter</author>
82
- <title>Paradox Lost</title>
83
- <genre>Science Fiction</genre>
84
- <price>6.95</price>
85
- <publish_date>2000-11-02</publish_date>
86
- <description>After an inadvertant trip through a Heisenberg
87
- Uncertainty Device, James Salway discovers the problems
88
- of being quantum.</description>
89
- </book>
90
- <book id="bk110">
91
- <author>O'Brien, Tim</author>
92
- <title>Microsoft .NET: The Programming Bible</title>
93
- <genre>Computer</genre>
94
- <price>36.95</price>
95
- <publish_date>2000-12-09</publish_date>
96
- <description>Microsoft's .NET initiative is explored in
97
- detail in this deep programmer's reference.</description>
98
- </book>
99
- <book id="bk111">
100
- <author>O'Brien, Tim</author>
101
- <title>MSXML3: A Comprehensive Guide</title>
102
- <genre>Computer</genre>
103
- <price>36.95</price>
104
- <publish_date>2000-12-01</publish_date>
105
- <description>The Microsoft MSXML3 parser is covered in
106
- detail, with attention to XML DOM interfaces, XSLT processing,
107
- SAX and more.</description>
108
- </book>
109
- <book id="bk112">
110
- <author>Galos, Mike</author>
111
- <title>Visual Studio 7: A Comprehensive Guide</title>
112
- <genre>Computer</genre>
113
- <price>49.95</price>
114
- <publish_date>2001-04-16</publish_date>
115
- <description>Microsoft Visual Studio 7 is explored in depth,
116
- looking at how Visual Basic, Visual C++, C#, and ASP+ are
117
- integrated into a comprehensive development
118
- environment.</description>
119
- </book>
120
- </catalog>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/fixtures/menu.html DELETED
@@ -1,15 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Document</title>
6
- </head>
7
- <body>
8
- <ul>
9
- <li><a href="http://example.com">Link 1</a></li>
10
- <li><a href="http://example.com">Link 2<!--- Comment 1 ---></a></li>
11
- <li><a href="http://example.com">Link 3<!--- Comment 2 ---></a></li>
12
- </ul>
13
- <!--- Comment --->
14
- </body>
15
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/imangazaliev/didom/tests/fixtures/posts.html DELETED
@@ -1,25 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>Posts</title>
6
- </head>
7
- <body>
8
- <h1 class="title">Posts</h1>
9
-
10
- <div class="posts">
11
- <div class="post">
12
- <h2 class="title">Lorem ipsum dolor sit amet.</h2>
13
- <p class="body">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore autem, in accusamus doloremque temporibus dolorem.</p>
14
- </div>
15
- <div class="post">
16
- <h2 class="title">Distinctio nisi ab in facere.</h2>
17
- <p class="body">Possimus adipisci atque voluptate non, voluptatum quam, saepe architecto repellat eum quaerat sed nam, quos.</p>
18
- </div>
19
- <div class="post">
20
- <h2 class="title">Consequatur eligendi praesentium voluptatem incidunt.</h2>
21
- <p class="body">Ad magnam optio maxime cupiditate eos eum. Perferendis voluptatum atque et nobis, facilis iusto! Ipsam.</p>
22
- </div>
23
- </div>
24
- </body>
25
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/kub-at/php-simple-html-dom-parser/README.md CHANGED
@@ -1,8 +1,8 @@
1
  php-simple-html-dom-parser
2
  ==========================
3
 
4
- Version 1.8.1 - PHP 7.3 compatible
5
- PHP Simple HTML DOM Parser changelog: https://sourceforge.net/projects/simplehtmldom/files/simplehtmldom/1.8.1/
6
 
7
 
8
  Install
1
  php-simple-html-dom-parser
2
  ==========================
3
 
4
+ Version 1.9.1 - PHP 7.3 compatible
5
+ PHP Simple HTML DOM Parser changelog: https://sourceforge.net/projects/simplehtmldom/files/simplehtmldom/1.9.1/
6
 
7
 
8
  Install
vendor/kub-at/php-simple-html-dom-parser/src/KubAT/PhpSimple/lib/simple_html_dom.php CHANGED
@@ -5,64 +5,24 @@ namespace simple_html_dom;
5
  * Website: http://sourceforge.net/projects/simplehtmldom/
6
  * Additional projects: http://sourceforge.net/projects/debugobject/
7
  * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
8
- * Contributions by:
9
- * Yousuke Kumakura (Attribute filters)
10
- * Vadim Voituk (Negative indexes supports of "find" method)
11
- * Antcs (Constructor with automatically load contents either text or file/url)
12
  *
13
- * all affected sections have comments starting with "PaperG"
14
- *
15
- * Paperg - Added case insensitive testing of the value of the selector.
16
- *
17
- * Paperg - Added tag_start for the starting index of tags - NOTE: This works
18
- * but not accurately. This tag_start gets counted AFTER \r\n have been crushed
19
- * out, and after the remove_noice calls so it will not reflect the REAL
20
- * position of the tag in the source, it will almost always be smaller by some
21
- * amount. We use this to determine how far into the file the tag in question
22
- * is. This "percentage" will never be accurate as the $dom->size is the "real"
23
- * number of bytes the dom was created from. But for most purposes, it's a
24
- * really good estimation.
25
- *
26
- * Paperg - Added the forceTagsClosed to the dom constructor. Forcing tags
27
- * closed is great for malformed html, but it CAN lead to parsing errors.
28
- *
29
- * Allow the user to tell us how much they trust the html.
30
- *
31
- * Paperg add the text and plaintext to the selectors for the find syntax.
32
- * plaintext implies text in the innertext of a node. text implies that the
33
- * tag is a text node. This allows for us to find tags based on the text they
34
- * contain.
35
- *
36
- * Create find_ancestor_tag to see if a tag is - at any level - inside of
37
- * another specific tag.
38
- *
39
- * Paperg: added parse_charset so that we know about the character set of
40
- * the source document. NOTE: If the user's system has a routine called
41
- * get_last_retrieve_url_contents_content_type availalbe, we will assume it's
42
- * returning the content-type header from the last transfer or curl_exec, and
43
- * we will parse that and use it in preference to any other method of charset
44
- * detection.
45
- *
46
- * Found infinite loop in the case of broken html in restore_noise. Rewrote to
47
- * protect from that.
48
  *
49
- * PaperG (John Schlick) Added get_display_size for "IMG" tags.
 
 
 
 
50
  *
51
- * Licensed under The MIT License
52
- * Redistributions of files must retain the above copyright notice.
 
 
53
  *
54
- * @author S.C. Chen <me578022@gmail.com>
55
- * @author John Schlick
56
- * @author Rus Carroll
57
- * @version Rev. 1.8.1 (247)
58
- * @package PlaceLocalInclude
59
- * @subpackage simple_html_dom
60
  */
61
 
62
- /**
63
- * All of the Defines for the classes below.
64
- * @author S.C. Chen <me578022@gmail.com>
65
- */
66
  define('HDOM_TYPE_ELEMENT', 1);
67
  define('HDOM_TYPE_COMMENT', 2);
68
  define('HDOM_TYPE_TEXT', 3);
@@ -81,25 +41,12 @@ define('HDOM_INFO_INNER', 5);
81
  define('HDOM_INFO_OUTER', 6);
82
  define('HDOM_INFO_ENDSPACE', 7);
83
 
84
- /** The default target charset */
85
  defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
86
-
87
- /** The default <br> text used instead of <br> tags when returning text */
88
  defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
89
-
90
- /** The default <span> text used instead of <span> tags when returning text */
91
  defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
92
-
93
- /** The maximum file size the parser should load */
94
  defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
95
-
96
- /** Contents between curly braces "{" and "}" are interpreted as text */
97
  define('HDOM_SMARTY_AS_TEXT', 1);
98
 
99
- // helper functions
100
- // -----------------------------------------------------------------------------
101
- // get html dom from file
102
- // $maxlen is defined in the code as PHP_STREAM_COPY_ALL which is defined as -1.
103
  function file_get_html(
104
  $url,
105
  $use_include_path = false,
@@ -113,10 +60,8 @@ function file_get_html(
113
  $defaultBRText = DEFAULT_BR_TEXT,
114
  $defaultSpanText = DEFAULT_SPAN_TEXT)
115
  {
116
- // Ensure maximum length is greater than zero
117
  if($maxLen <= 0) { $maxLen = MAX_FILE_SIZE; }
118
 
119
- // We DO force the tags to be terminated.
120
  $dom = new simple_html_dom(
121
  null,
122
  $lowercase,
@@ -124,7 +69,8 @@ function file_get_html(
124
  $target_charset,
125
  $stripRN,
126
  $defaultBRText,
127
- $defaultSpanText);
 
128
 
129
  /**
130
  * For sourceforge users: uncomment the next line and comment the
@@ -135,19 +81,18 @@ function file_get_html(
135
  $use_include_path,
136
  $context,
137
  $offset,
138
- $maxLen);
139
-
140
- // Paperg - use our own mechanism for getting the contents as we want to
141
- // control the timeout.
142
  // $contents = retrieve_url_contents($url);
143
- if (empty($contents) || strlen($contents) > $maxLen) { return false; }
144
 
145
- // The second parameter can force the selectors to all be lowercase.
146
- $dom->load($contents, $lowercase, $stripRN);
147
- return $dom;
 
 
 
148
  }
149
 
150
- // get html dom from string
151
  function str_get_html(
152
  $str,
153
  $lowercase = true,
@@ -164,97 +109,34 @@ function str_get_html(
164
  $target_charset,
165
  $stripRN,
166
  $defaultBRText,
167
- $defaultSpanText);
 
168
 
169
  if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
170
  $dom->clear();
171
  return false;
172
  }
173
 
174
- $dom->load($str, $lowercase, $stripRN);
175
- return $dom;
176
  }
177
 
178
- // dump html dom tree
179
  function dump_html_tree($node, $show_attr = true, $deep = 0)
180
  {
181
  $node->dump($node);
182
  }
183
 
184
- /**
185
- * simple html dom node
186
- * PaperG - added ability for "find" routine to lowercase the value of the
187
- * selector.
188
- *
189
- * PaperG - added $tag_start to track the start position of the tag in the total
190
- * byte index
191
- *
192
- * @package PlaceLocalInclude
193
- */
194
  class simple_html_dom_node
195
  {
196
- /**
197
- * Node type
198
- *
199
- * Default is {@see HDOM_TYPE_TEXT}
200
- *
201
- * @var int
202
- */
203
  public $nodetype = HDOM_TYPE_TEXT;
204
-
205
- /**
206
- * Tag name
207
- *
208
- * Default is 'text'
209
- *
210
- * @var string
211
- */
212
  public $tag = 'text';
213
-
214
- /**
215
- * List of attributes
216
- *
217
- * @var array
218
- */
219
  public $attr = array();
220
-
221
- /**
222
- * List of child node objects
223
- *
224
- * @var array
225
- */
226
  public $children = array();
227
  public $nodes = array();
228
-
229
- /**
230
- * The parent node object
231
- *
232
- * @var object|null
233
- */
234
  public $parent = null;
235
-
236
- // The "info" array - see HDOM_INFO_... for what each element contains.
237
  public $_ = array();
238
-
239
- /**
240
- * Start position of the tag in the document
241
- *
242
- * @var int
243
- */
244
  public $tag_start = 0;
245
-
246
- /**
247
- * The DOM object
248
- *
249
- * @var object|null
250
- */
251
  private $dom = null;
252
 
253
- /**
254
- * Construct new node object
255
- *
256
- * Adds itself to the list of DOM Nodes {@see simple_html_dom::$nodes}
257
- */
258
  function __construct($dom)
259
  {
260
  $this->dom = $dom;
@@ -271,7 +153,6 @@ class simple_html_dom_node
271
  return $this->outertext();
272
  }
273
 
274
- // clean up memory due to php5 circular references memory leak...
275
  function clear()
276
  {
277
  $this->dom = null;
@@ -280,17 +161,14 @@ class simple_html_dom_node
280
  $this->children = null;
281
  }
282
 
283
- // dump node's tree
284
- function dump($show_attr = true, $deep = 0)
285
  {
286
- $lead = str_repeat(' ', $deep);
287
-
288
- echo $lead . $this->tag;
289
 
290
  if ($show_attr && count($this->attr) > 0) {
291
  echo '(';
292
  foreach ($this->attr as $k => $v) {
293
- echo "[$k]=>\"" . $this->$k . '", ';
294
  }
295
  echo ')';
296
  }
@@ -298,14 +176,12 @@ class simple_html_dom_node
298
  echo "\n";
299
 
300
  if ($this->nodes) {
301
- foreach ($this->nodes as $c) {
302
- $c->dump($show_attr, $deep + 1);
303
  }
304
  }
305
  }
306
 
307
-
308
- // Debugging function to dump a single dom node with a bunch of information about it.
309
  function dump_node($echo = true)
310
  {
311
  $string = $this->tag;
@@ -313,7 +189,7 @@ class simple_html_dom_node
313
  if (count($this->attr) > 0) {
314
  $string .= '(';
315
  foreach ($this->attr as $k => $v) {
316
- $string .= "[$k]=>\"" . $this->$k . '", ';
317
  }
318
  $string .= ')';
319
  }
@@ -324,24 +200,24 @@ class simple_html_dom_node
324
  if (is_array($v)) {
325
  $string .= "[$k]=>(";
326
  foreach ($v as $k2 => $v2) {
327
- $string .= "[$k2]=>\"" . $v2 . '", ';
328
  }
329
  $string .= ')';
330
  } else {
331
- $string .= "[$k]=>\"" . $v . '", ';
332
  }
333
  }
334
  $string .= ')';
335
  }
336
 
337
  if (isset($this->text)) {
338
- $string .= ' text: (' . $this->text . ')';
339
  }
340
 
341
- $string .= " HDOM_INNER_INFO: '";
342
 
343
  if (isset($node->_[HDOM_INFO_INNER])) {
344
- $string .= $node->_[HDOM_INFO_INNER] . "'";
345
  } else {
346
  $string .= ' NULL ';
347
  }
@@ -359,13 +235,6 @@ class simple_html_dom_node
359
  }
360
  }
361
 
362
- /**
363
- * Return or set parent node
364
- *
365
- * @param object|null $parent (optional) The parent node, `null` to return
366
- * the current parent node.
367
- * @return object|null The parent node
368
- */
369
  function parent($parent = null)
370
  {
371
  // I am SURE that this doesn't work properly.
@@ -380,22 +249,11 @@ class simple_html_dom_node
380
  return $this->parent;
381
  }
382
 
383
- /**
384
- * @return bool True if the node has at least one child node
385
- */
386
  function has_child()
387
  {
388
  return !empty($this->children);
389
  }
390
 
391
- /**
392
- * Get child node at specified index
393
- *
394
- * @param int $idx The index of the child node to return, `-1` to return all
395
- * child nodes.
396
- * @return object|array|null The child node at the specified index, all child
397
- * nodes or null if the index is invalid.
398
- */
399
  function children($idx = -1)
400
  {
401
  if ($idx === -1) {
@@ -409,15 +267,6 @@ class simple_html_dom_node
409
  return null;
410
  }
411
 
412
- /**
413
- * Get first child node
414
- *
415
- * @return object|null The first child node or null if the current node has
416
- * no child nodes.
417
- *
418
- * @todo Use `empty()` instead of `count()` to improve performance on large
419
- * arrays.
420
- */
421
  function first_child()
422
  {
423
  if (count($this->children) > 0) {
@@ -426,108 +275,70 @@ class simple_html_dom_node
426
  return null;
427
  }
428
 
429
- /**
430
- * Get last child node
431
- *
432
- * @return object|null The last child node or null if the current node has
433
- * no child nodes.
434
- *
435
- * @todo Use `end()` to slightly improve performance on large arrays.
436
- */
437
  function last_child()
438
  {
439
- if (($count = count($this->children)) > 0) {
440
- return $this->children[$count - 1];
441
  }
442
  return null;
443
  }
444
 
445
- /**
446
- * Get next sibling node
447
- *
448
- * @return object|null The sibling node or null if the current node has no
449
- * sibling nodes.
450
- */
451
  function next_sibling()
452
  {
453
  if ($this->parent === null) {
454
  return null;
455
  }
456
 
457
- $idx = 0;
458
- $count = count($this->parent->children);
459
 
460
- while ($idx < $count && $this !== $this->parent->children[$idx]) {
461
- ++$idx;
462
  }
463
 
464
- if (++$idx >= $count) {
465
- return null;
466
- }
467
-
468
- return $this->parent->children[$idx];
469
  }
470
 
471
- /**
472
- * Get previous sibling node
473
- *
474
- * @return object|null The sibling node or null if the current node has no
475
- * sibling nodes.
476
- */
477
  function prev_sibling()
478
  {
479
- if ($this->parent === null) { return null; }
 
 
480
 
481
- $idx = 0;
482
- $count = count($this->parent->children);
483
 
484
- while ($idx < $count && $this !== $this->parent->children[$idx]) {
485
- ++$idx;
486
  }
487
 
488
- if (--$idx < 0) { return null; }
489
-
490
- return $this->parent->children[$idx];
491
  }
492
 
493
- /**
494
- * Traverse ancestors to the first matching tag.
495
- *
496
- * @param string $tag Tag to find
497
- * @return object|null First matching node in the DOM tree or null if no
498
- * match was found.
499
- *
500
- * @todo Null is returned implicitly by calling ->parent on the root node.
501
- * This behaviour could change at any time, rendering this function invalid.
502
- */
503
  function find_ancestor_tag($tag)
504
  {
505
  global $debug_object;
506
  if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
507
 
508
- // Start by including ourselves in the comparison.
509
- $returnDom = $this;
 
510
 
511
- while (!is_null($returnDom)) {
 
 
512
  if (is_object($debug_object)) {
513
- $debug_object->debug_log(2, 'Current tag is: ' . $returnDom->tag);
514
  }
515
 
516
- if ($returnDom->tag == $tag) {
517
  break;
518
  }
519
 
520
- $returnDom = $returnDom->parent;
521
  }
522
 
523
- return $returnDom;
524
  }
525
 
526
- /**
527
- * Get node's inner text (everything inside the opening and closing tags)
528
- *
529
- * @return string
530
- */
531
  function innertext()
532
  {
533
  if (isset($this->_[HDOM_INFO_INNER])) {
@@ -547,11 +358,6 @@ class simple_html_dom_node
547
  return $ret;
548
  }
549
 
550
- /**
551
- * Get node's outer text (everything including the opening and closing tags)
552
- *
553
- * @return string
554
- */
555
  function outertext()
556
  {
557
  global $debug_object;
@@ -568,9 +374,11 @@ class simple_html_dom_node
568
  $debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
569
  }
570
 
571
- if ($this->tag === 'root') return $this->innertext();
 
 
572
 
573
- // trigger callback
574
  if ($this->dom && $this->dom->callback !== null) {
575
  call_user_func_array($this->dom->callback, array($this));
576
  }
@@ -583,29 +391,23 @@ class simple_html_dom_node
583
  return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
584
  }
585
 
586
- // render begin tag
 
587
  if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
588
  $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
589
- } else {
590
- $ret = '';
591
  }
592
 
593
- // render inner text
594
  if (isset($this->_[HDOM_INFO_INNER])) {
595
- // If it's a br tag... don't return the HDOM_INNER_INFO that we
596
- // may or may not have added.
597
  if ($this->tag !== 'br') {
598
  $ret .= $this->_[HDOM_INFO_INNER];
599
  }
600
- } else {
601
- if ($this->nodes) {
602
- foreach ($this->nodes as $n) {
603
- $ret .= $this->convert_text($n->outertext());
604
- }
605
  }
606
  }
607
 
608
- // render end tag
609
  if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
610
  $ret .= '</' . $this->tag . '>';
611
  }
@@ -613,11 +415,6 @@ class simple_html_dom_node
613
  return $ret;
614
  }
615
 
616
- /**
617
- * Get node's plain text (everything excluding all tags)
618
- *
619
- * @return string
620
- */
621
  function text()
622
  {
623
  if (isset($this->_[HDOM_INFO_INNER])) {
@@ -644,7 +441,7 @@ class simple_html_dom_node
644
  foreach ($this->nodes as $n) {
645
  // Start paragraph after a blank line
646
  if ($n->tag === 'p') {
647
- $ret .= "\n\n";
648
  }
649
 
650
  $ret .= $this->convert_text($n->text());
@@ -657,14 +454,9 @@ class simple_html_dom_node
657
  }
658
  }
659
  }
660
- return trim($ret);
661
  }
662
 
663
- /**
664
- * Get node's xml text (inner text as a CDATA section)
665
- *
666
- * @return string
667
- */
668
  function xmltext()
669
  {
670
  $ret = $this->innertext();
@@ -673,7 +465,6 @@ class simple_html_dom_node
673
  return $ret;
674
  }
675
 
676
- // build node's text with tag
677
  function makeup()
678
  {
679
  // text, comment, unknown
@@ -717,18 +508,6 @@ class simple_html_dom_node
717
  return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
718
  }
719
 
720
- /**
721
- * Find elements by CSS selector
722
- *
723
- * @param string $selector The CSS selector
724
- * @param int|null $idx Index of element to return form the list of matching
725
- * elements (default: `null` = disabled).
726
- * @param bool $lowercase Matches tag names case insensitive (lowercase) if
727
- * enabled (default: `false`)
728
- * @return array|object|null A list of elements matching the specified CSS
729
- * selector or a single element if $idx is specified or null if no element
730
- * was found.
731
- */
732
  function find($selector, $idx = null, $lowercase = false)
733
  {
734
  $selectors = $this->parse_selector($selector);
@@ -781,19 +560,6 @@ class simple_html_dom_node
781
  return (isset($found[$idx])) ? $found[$idx] : null;
782
  }
783
 
784
- /**
785
- * Seek DOM elements by selector
786
- *
787
- * **Note**
788
- * The selector element must be compatible to a selector from
789
- * {@see simple_html_dom_node::parse_selector()}
790
- *
791
- * @param array $selector A selector element
792
- * @param array $ret An array of matches
793
- * @param bool $lowercase Matches tag names case insensitive (lowercase) if
794
- * enabled (default: `false`)
795
- * @return void
796
- */
797
  protected function seek($selector, &$ret, $parent_cmd, $lowercase = false)
798
  {
799
  global $debug_object;
@@ -825,7 +591,8 @@ class simple_html_dom_node
825
  && $this->parent
826
  && in_array($this, $this->parent->children)) { // Next-Sibling Combinator
827
  $index = array_search($this, $this->parent->children, true) + 1;
828
- $nodes[] = $this->parent->children[$index];
 
829
  } elseif ($parent_cmd === '~'
830
  && $this->parent
831
  && in_array($this, $this->parent->children)) { // Subsequent Sibling Combinator
@@ -844,6 +611,13 @@ class simple_html_dom_node
844
  $pass = false;
845
  }
846
 
 
 
 
 
 
 
 
847
  // Skip if node isn't a child node (i.e. text nodes)
848
  if($pass && !in_array($node, $node->parent->children, true)) {
849
  $pass = false;
@@ -1008,24 +782,6 @@ class simple_html_dom_node
1008
  }
1009
  }
1010
 
1011
- /**
1012
- * Match value and pattern for a given CSS expression
1013
- *
1014
- * **Supported Expressions**
1015
- *
1016
- * | Expression | Description
1017
- * | ---------- | -----------
1018
- * | `=` | $value and $pattern must be equal
1019
- * | `!=` | $value and $pattern must not be equal
1020
- * | `^=` | $value must start with $pattern
1021
- * | `$=` | $value must end with $pattern
1022
- * | `*=` | $value must contain $pattern
1023
- *
1024
- * @param string $exp The expression.
1025
- * @param string $pattern The pattern
1026
- * @param string $value The value
1027
- * @value bool True if $value matches $pattern
1028
- */
1029
  protected function match($exp, $pattern, $value, $case_sensitivity)
1030
  {
1031
  global $debug_object;
@@ -1071,31 +827,6 @@ class simple_html_dom_node
1071
  return false;
1072
  }
1073
 
1074
- /**
1075
- * Parse CSS selector
1076
- *
1077
- * @param string $selector_string CSS selector string
1078
- * @return array List of CSS selectors. The format depends on the type of
1079
- * selector:
1080
- *
1081
- * ```php
1082
- *
1083
- * array( // list of selectors (each separated by a comma), i.e. 'img, p, div'
1084
- * array( // list of combinator selectors, i.e. 'img > p > div'
1085
- * array( // selector element
1086
- * [0], // (string) The element tag
1087
- * [1], // (string) The element id
1088
- * [2], // (array<string>) The element classes
1089
- * [3], // (array<array<string>>) The list of attributes, each
1090
- * // with four elements: name, expression, value, inverted
1091
- * [4] // (string) The selector combinator (' ' | '>' | '+' | '~')
1092
- * )
1093
- * )
1094
- * )
1095
- * ```
1096
- *
1097
- * @link https://www.w3.org/TR/selectors/#compound Compound selector
1098
- */
1099
  protected function parse_selector($selector_string)
1100
  {
1101
  global $debug_object;
@@ -1187,7 +918,7 @@ class simple_html_dom_node
1187
  */
1188
  if($m[4] !== '') {
1189
  preg_match_all(
1190
- "/\[@?(!?[\w:-]+)(?:([!*^$|~]?=)[\"']?(.*?)[\"']?)?(?:\s*?([iIsS])?)?\]/is",
1191
  trim($m[4]),
1192
  $attributes,
1193
  PREG_SET_ORDER
@@ -1287,8 +1018,6 @@ class simple_html_dom_node
1287
  if (isset($this->attr[$name])) { unset($this->attr[$name]); }
1288
  }
1289
 
1290
- // PaperG - Function to convert the text from one character set to another
1291
- // if the two sets are not the same.
1292
  function convert_text($text)
1293
  {
1294
  global $debug_object;
@@ -1339,12 +1068,6 @@ class simple_html_dom_node
1339
  return $converted_text;
1340
  }
1341
 
1342
- /**
1343
- * Returns true if $string is valid UTF-8 and false otherwise.
1344
- *
1345
- * @param mixed $str String to be tested
1346
- * @return boolean
1347
- */
1348
  static function is_utf8($str)
1349
  {
1350
  $c = 0; $b = 0;
@@ -1372,16 +1095,6 @@ class simple_html_dom_node
1372
  return true;
1373
  }
1374
 
1375
- /**
1376
- * Function to try a few tricks to determine the displayed size of an img on
1377
- * the page. NOTE: This will ONLY work on an IMG tag. Returns FALSE on all
1378
- * other tag types.
1379
- *
1380
- * @author John Schlick
1381
- * @version April 19 2012
1382
- * @return array an array containing the 'height' and 'width' of the image
1383
- * on the page or -1 if we can't figure it out.
1384
- */
1385
  function get_display_size()
1386
  {
1387
  global $debug_object;
@@ -1467,7 +1180,82 @@ class simple_html_dom_node
1467
  return $result;
1468
  }
1469
 
1470
- // camel naming conventions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1471
  function getAllAttributes()
1472
  {
1473
  return $this->attr;
@@ -1493,6 +1281,44 @@ class simple_html_dom_node
1493
  $this->__set($name, null);
1494
  }
1495
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1496
  function getElementById($id)
1497
  {
1498
  return $this->find("#$id", 0);
@@ -1561,170 +1387,34 @@ class simple_html_dom_node
1561
 
1562
  }
1563
 
1564
- /**
1565
- * simple html dom parser
1566
- *
1567
- * Paperg - in the find routine: allow us to specify that we want case
1568
- * insensitive testing of the value of the selector.
1569
- *
1570
- * Paperg - change $size from protected to public so we can easily access it
1571
- *
1572
- * Paperg - added ForceTagsClosed in the constructor which tells us whether we
1573
- * trust the html or not. Default is to NOT trust it.
1574
- *
1575
- * @package PlaceLocalInclude
1576
- */
1577
  class simple_html_dom
1578
  {
1579
- /**
1580
- * The root node of the document
1581
- *
1582
- * @var object
1583
- */
1584
  public $root = null;
1585
-
1586
- /**
1587
- * List of nodes in the current DOM
1588
- *
1589
- * @var array
1590
- */
1591
  public $nodes = array();
1592
-
1593
- /**
1594
- * Callback function to run for each element in the DOM.
1595
- *
1596
- * @var callable|null
1597
- */
1598
  public $callback = null;
1599
-
1600
- /**
1601
- * Indicates how tags and attributes are matched
1602
- *
1603
- * @var bool When set to **true** tags and attributes will be converted to
1604
- * lowercase before matching.
1605
- */
1606
  public $lowercase = false;
1607
-
1608
- /**
1609
- * Original document size
1610
- *
1611
- * Holds the original document size.
1612
- *
1613
- * @var int
1614
- */
1615
  public $original_size;
1616
-
1617
- /**
1618
- * Current document size
1619
- *
1620
- * Holds the current document size. The document size is determined by the
1621
- * string length of ({@see simple_html_dom::$doc}).
1622
- *
1623
- * _Note_: Using this variable is more efficient than calling `strlen($doc)`
1624
- *
1625
- * @var int
1626
- * */
1627
  public $size;
1628
 
1629
- /**
1630
- * Current position in the document
1631
- *
1632
- * @var int
1633
- */
1634
  protected $pos;
1635
-
1636
- /**
1637
- * The document
1638
- *
1639
- * @var string
1640
- */
1641
  protected $doc;
1642
-
1643
- /**
1644
- * Current character
1645
- *
1646
- * Holds the current character at position {@see simple_html_dom::$pos} in
1647
- * the document {@see simple_html_dom::$doc}
1648
- *
1649
- * _Note_: Using this variable is more efficient than calling
1650
- * `substr($doc, $pos, 1)`
1651
- *
1652
- * @var string
1653
- */
1654
  protected $char;
1655
 
1656
  protected $cursor;
1657
-
1658
- /**
1659
- * Parent node of the next node detected by the parser
1660
- *
1661
- * @var object
1662
- */
1663
  protected $parent;
1664
  protected $noise = array();
1665
-
1666
- /**
1667
- * Tokens considered blank in HTML
1668
- *
1669
- * @var string
1670
- */
1671
  protected $token_blank = " \t\r\n";
1672
-
1673
- /**
1674
- * Tokens to identify the equal sign for attributes, stopping either at the
1675
- * closing tag ("/" i.e. "<html />") or the end of an opening tag (">" i.e.
1676
- * "<html>")
1677
- *
1678
- * @var string
1679
- */
1680
  protected $token_equal = ' =/>';
1681
-
1682
- /**
1683
- * Tokens to identify the end of a tag name. A tag name either ends on the
1684
- * ending slash ("/" i.e. "<html/>") or whitespace ("\s\r\n\t")
1685
- *
1686
- * @var string
1687
- */
1688
  protected $token_slash = " />\r\n\t";
1689
-
1690
- /**
1691
- * Tokens to identify the end of an attribute
1692
- *
1693
- * @var string
1694
- */
1695
  protected $token_attr = ' >';
1696
 
1697
- // Note that this is referenced by a child node, and so it needs to be
1698
- // public for that node to see this information.
1699
  public $_charset = '';
1700
  public $_target_charset = '';
1701
 
1702
- /**
1703
- * Innertext for <br> elements
1704
- *
1705
- * @var string
1706
- */
1707
  protected $default_br_text = '';
1708
 
1709
- /**
1710
- * Suffix for <span> elements
1711
- *
1712
- * @var string
1713
- */
1714
  public $default_span_text = '';
1715
 
1716
- /**
1717
- * Defines a list of self-closing tags (Void elements) according to the HTML
1718
- * Specification
1719
- *
1720
- * _Remarks_:
1721
- * - Use `isset()` instead of `in_array()` on array elements to boost
1722
- * performance about 30%
1723
- * - Sort elements by name for better readability!
1724
- *
1725
- * @link https://www.w3.org/TR/html HTML Specification
1726
- * @link https://www.w3.org/TR/html/syntax.html#void-elements Void elements
1727
- */
1728
  protected $self_closing_tags = array(
1729
  'area' => 1,
1730
  'base' => 1,
@@ -1741,18 +1431,6 @@ class simple_html_dom
1741
  'track' => 1,
1742
  'wbr' => 1
1743
  );
1744
-
1745
- /**
1746
- * Defines a list of tags which - if closed - close all optional closing
1747
- * elements within if they haven't been closed yet. (So, an element where
1748
- * neither opening nor closing tag is omissible consistently closes every
1749
- * optional closing element within)
1750
- *
1751
- * _Remarks_:
1752
- * - Use `isset()` instead of `in_array()` on array elements to boost
1753
- * performance about 30%
1754
- * - Sort elements by name for better readability!
1755
- */
1756
  protected $block_tags = array(
1757
  'body' => 1,
1758
  'div' => 1,
@@ -1761,62 +1439,6 @@ class simple_html_dom
1761
  'span' => 1,
1762
  'table' => 1
1763
  );
1764
-
1765
- /**
1766
- * Defines elements whose end tag is omissible.
1767
- *
1768
- * * key = Name of an element whose end tag is omissible.
1769
- * * value = Names of elements whose end tag is omissible, that are closed
1770
- * by the current element.
1771
- *
1772
- * _Remarks_:
1773
- * - Use `isset()` instead of `in_array()` on array elements to boost
1774
- * performance about 30%
1775
- * - Sort elements by name for better readability!
1776
- *
1777
- * **Example**
1778
- *
1779
- * An `li` element’s end tag may be omitted if the `li` element is immediately
1780
- * followed by another `li` element. To do that, add following element to the
1781
- * array:
1782
- *
1783
- * ```php
1784
- * 'li' => array('li'),
1785
- * ```
1786
- *
1787
- * With this, the following two examples are considered equal. Note that the
1788
- * second example is missing the closing tags on `li` elements.
1789
- *
1790
- * ```html
1791
- * <ul><li>First Item</li><li>Second Item</li></ul>
1792
- * ```
1793
- *
1794
- * <ul><li>First Item</li><li>Second Item</li></ul>
1795
- *
1796
- * ```html
1797
- * <ul><li>First Item<li>Second Item</ul>
1798
- * ```
1799
- *
1800
- * <ul><li>First Item<li>Second Item</ul>
1801
- *
1802
- * @var array A two-dimensional array where the key is the name of an
1803
- * element whose end tag is omissible and the value is an array of elements
1804
- * whose end tag is omissible, that are closed by the current element.
1805
- *
1806
- * @link https://www.w3.org/TR/html/syntax.html#optional-tags Optional tags
1807
- *
1808
- * @todo The implementation of optional closing tags doesn't work in all cases
1809
- * because it only consideres elements who close other optional closing
1810
- * tags, not taking into account that some (non-blocking) tags should close
1811
- * these optional closing tags. For example, the end tag for "p" is omissible
1812
- * and can be closed by an "address" element, whose end tag is NOT omissible.
1813
- * Currently a "p" element without closing tag stops at the next "p" element
1814
- * or blocking tag, even if it contains other elements.
1815
- *
1816
- * @todo Known sourceforge issue #2977341
1817
- * B tags that are not closed cause us to return everything to the end of
1818
- * the document.
1819
- */
1820
  protected $optional_closing_tags = array(
1821
  // Not optional, see
1822
  // https://www.w3.org/TR/html/textlevel-semantics.html#the-b-element
@@ -1875,7 +1497,6 @@ class simple_html_dom
1875
  $this->clear();
1876
  }
1877
 
1878
- // load html from string
1879
  function load(
1880
  $str,
1881
  $lowercase = true,
@@ -1930,7 +1551,6 @@ class simple_html_dom
1930
  return $this;
1931
  }
1932
 
1933
- // load html from file
1934
  function load_file()
1935
  {
1936
  $args = func_get_args();
@@ -1942,29 +1562,16 @@ class simple_html_dom
1942
  }
1943
  }
1944
 
1945
- /**
1946
- * Set the callback function
1947
- *
1948
- * @param callable $function_name Callback function to run for each element
1949
- * in the DOM.
1950
- * @return void
1951
- */
1952
  function set_callback($function_name)
1953
  {
1954
  $this->callback = $function_name;
1955
  }
1956
 
1957
- /**
1958
- * Remove callback function
1959
- *
1960
- * @return void
1961
- */
1962
  function remove_callback()
1963
  {
1964
  $this->callback = null;
1965
  }
1966
 
1967
- // save dom as string
1968
  function save($filepath = '')
1969
  {
1970
  $ret = $this->root->innertext();
@@ -1972,18 +1579,18 @@ class simple_html_dom
1972
  return $ret;
1973
  }
1974
 
1975
- // find dom node by css selector
1976
- // Paperg - allow us to specify that we want case insensitive testing of the value of the selector.
1977
  function find($selector, $idx = null, $lowercase = false)
1978
  {
1979
  return $this->root->find($selector, $idx, $lowercase);
1980
  }
1981
 
1982
- // clean up memory due to php5 circular references memory leak...
1983
  function clear()
1984
  {
1985
- foreach ($this->nodes as $n) {
1986
- $n->clear(); $n = null;
 
 
 
1987
  }
1988
 
1989
  // This add next line is documented in the sourceforge repository.
@@ -1991,7 +1598,8 @@ class simple_html_dom
1991
  // use of clear.
1992
  if (isset($this->children)) {
1993
  foreach ($this->children as $n) {
1994
- $n->clear(); $n = null;
 
1995
  }
1996
  }
1997
 
@@ -2014,7 +1622,6 @@ class simple_html_dom
2014
  $this->root->dump($show_attr);
2015
  }
2016
 
2017
- // prepare HTML data and init everything
2018
  protected function prepare(
2019
  $str, $lowercase = true,
2020
  $defaultBRText = DEFAULT_BR_TEXT,
@@ -2040,11 +1647,6 @@ class simple_html_dom
2040
  if ($this->size > 0) { $this->char = $this->doc[0]; }
2041
  }
2042
 
2043
- /**
2044
- * Parse HTML content
2045
- *
2046
- * @return bool True on success
2047
- */
2048
  protected function parse()
2049
  {
2050
  while (true) {
@@ -2066,13 +1668,6 @@ class simple_html_dom
2066
  }
2067
  }
2068
 
2069
- // PAPERG - dkchou - added this to try to identify the character set of the
2070
- // page we have just parsed so we know better how to spit it out later.
2071
- // NOTE: IF you provide a routine called
2072
- // get_last_retrieve_url_contents_content_type which returns the
2073
- // CURLINFO_CONTENT_TYPE from the last curl_exec
2074
- // (or the content_type header from the last transfer), we will parse THAT,
2075
- // and if a charset is specified, we will use it over any other mechanism.
2076
  protected function parse_charset()
2077
  {
2078
  global $debug_object;
@@ -2094,6 +1689,7 @@ class simple_html_dom
2094
  }
2095
 
2096
  if (empty($charset)) {
 
2097
  $el = $this->root->find('meta[http-equiv=Content-Type]', 0, true);
2098
 
2099
  if (!empty($el)) {
@@ -2130,53 +1726,77 @@ class simple_html_dom
2130
  }
2131
  }
2132
 
2133
- // If we couldn't find a charset above, then lets try to detect one
2134
- // based on the text we got...
2135
  if (empty($charset)) {
2136
- // Use this in case mb_detect_charset isn't installed/loaded on
2137
- // this machine.
2138
- $charset = false;
 
 
 
 
 
 
 
 
 
2139
  if (function_exists('mb_detect_encoding')) {
2140
- // Have php try to detect the encoding from the text given to us.
2141
- $charset = mb_detect_encoding(
2142
- $this->doc . 'ascii',
2143
- $encoding_list = array( 'UTF-8', 'CP1252' )
 
 
 
 
 
 
 
 
 
 
 
 
 
2144
  );
2145
 
2146
- if (is_object($debug_object)) {
2147
- $debug_object->debug_log(2, 'mb_detect found: ' . $charset);
 
 
 
 
 
 
2148
  }
2149
- }
2150
 
2151
- // and if this doesn't work... then we need to just wrongheadedly
2152
- // assume it's UTF-8 so that we can move on - cause this will
2153
- // usually give us most of what we need...
2154
- if ($charset === false) {
2155
- if (is_object($debug_object)) {
2156
- $debug_object->debug_log(
2157
- 2,
2158
- 'since mb_detect failed - using default of utf-8'
2159
- );
2160
  }
 
 
2161
 
2162
- $charset = 'UTF-8';
 
 
 
 
2163
  }
2164
  }
2165
 
2166
  // Since CP1252 is a superset, if we get one of it's subsets, we want
2167
  // it instead.
2168
- if ((strtolower($charset) == strtolower('ISO-8859-1'))
2169
- || (strtolower($charset) == strtolower('Latin1'))
2170
- || (strtolower($charset) == strtolower('Latin-1'))) {
2171
-
2172
  if (is_object($debug_object)) {
2173
- $debug_object->debug_log(
2174
- 2,
2175
  'replacing ' . $charset . ' with CP1252 as its a superset'
2176
  );
2177
  }
2178
-
2179
- $charset = 'CP1252';
2180
  }
2181
 
2182
  if (is_object($debug_object)) {
@@ -2186,11 +1806,6 @@ class simple_html_dom
2186
  return $this->_charset = $charset;
2187
  }
2188
 
2189
- /**
2190
- * Parse tag from current document position.
2191
- *
2192
- * @return bool True if a tag was found, false otherwise
2193
- */
2194
  protected function read_tag()
2195
  {
2196
  // Set end position if no further tags found
@@ -2469,63 +2084,50 @@ class simple_html_dom
2469
  return true;
2470
  }
2471
 
2472
- /**
2473
- * Parse attribute from current document position
2474
- *
2475
- * @param object $node Node for the attributes
2476
- * @param string $name Name of the current attribute
2477
- * @param array $space Array for spacing information
2478
- * @return void
2479
- */
2480
  protected function parse_attr($node, $name, &$space)
2481
  {
2482
- // Per sourceforge: http://sourceforge.net/tracker/?func=detail&aid=3061408&group_id=218559&atid=1044037
2483
- // If the attribute is already defined inside a tag, only pay attention
2484
- // to the first one as opposed to the last one.
2485
- // https://stackoverflow.com/a/26341866
2486
- if (isset($node->attr[$name])) {
2487
- return;
2488
- }
2489
 
2490
- // [2] Whitespace between "=" and the value
2491
- $space[2] = $this->copy_skip($this->token_blank);
2492
 
2493
  switch ($this->char) {
2494
- case '"': // value is anything between double quotes
2495
- $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
2496
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2497
- $node->attr[$name] = $this->restore_noise($this->copy_until_char('"'));
2498
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2499
  break;
2500
- case '\'': // value is anything between single quotes
2501
- $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_SINGLE;
2502
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2503
- $node->attr[$name] = $this->restore_noise($this->copy_until_char('\''));
2504
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2505
  break;
2506
- default: // value is anything until the first space or end tag
2507
- $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
2508
- $node->attr[$name] = $this->restore_noise($this->copy_until($this->token_attr));
2509
  }
 
 
 
2510
  // PaperG: Attributes should not have \r or \n in them, that counts as
2511
  // html whitespace.
2512
- $node->attr[$name] = str_replace("\r", '', $node->attr[$name]);
2513
- $node->attr[$name] = str_replace("\n", '', $node->attr[$name]);
 
2514
  // PaperG: If this is a "class" selector, lets get rid of the preceeding
2515
  // and trailing space since some people leave it in the multi class case.
2516
  if ($name === 'class') {
2517
- $node->attr[$name] = trim($node->attr[$name]);
 
 
 
 
 
2518
  }
2519
  }
2520
 
2521
- /**
2522
- * Link node to parent node
2523
- *
2524
- * @param object $node Node to link to parent
2525
- * @param bool $is_child True if the node is a child of parent
2526
- * @return void
2527
- */
2528
- // link node's parent
2529
  protected function link_nodes(&$node, $is_child)
2530
  {
2531
  $node->parent = $this->parent;
@@ -2535,12 +2137,6 @@ class simple_html_dom
2535
  }
2536
  }
2537
 
2538
- /**
2539
- * Add tag as text node to current node
2540
- *
2541
- * @param string $tag Tag name
2542
- * @return bool True on success
2543
- */
2544
  protected function as_text_node($tag)
2545
  {
2546
  $node = new simple_html_dom_node($this);
@@ -2551,28 +2147,12 @@ class simple_html_dom
2551
  return true;
2552
  }
2553
 
2554
- /**
2555
- * Seek from the current document position to the first occurrence of a
2556
- * character not defined by the provided string. Update the current document
2557
- * position to the new position.
2558
- *
2559
- * @param string $chars A string containing every allowed character.
2560
- * @return void
2561
- */
2562
  protected function skip($chars)
2563
  {
2564
  $this->pos += strspn($this->doc, $chars, $this->pos);
2565
  $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2566
  }
2567
 
2568
- /**
2569
- * Copy substring from the current document position to the first occurrence
2570
- * of a character not defined by the provided string.
2571
- *
2572
- * @param string $chars A string containing every allowed character.
2573
- * @return string Substring from the current document position to the first
2574
- * occurrence of a character not defined by the provided string.
2575
- */
2576
  protected function copy_skip($chars)
2577
  {
2578
  $pos = $this->pos;
@@ -2583,14 +2163,6 @@ class simple_html_dom
2583
  return substr($this->doc, $pos, $len);
2584
  }
2585
 
2586
- /**
2587
- * Copy substring from the current document position to the first occurrence
2588
- * of any of the provided characters.
2589
- *
2590
- * @param string $chars A string containing every character to stop at.
2591
- * @return string Substring from the current document position to the first
2592
- * occurrence of any of the provided characters.
2593
- */
2594
  protected function copy_until($chars)
2595
  {
2596
  $pos = $this->pos;
@@ -2600,14 +2172,6 @@ class simple_html_dom
2600
  return substr($this->doc, $pos, $len);
2601
  }
2602
 
2603
- /**
2604
- * Copy substring from the current document position to the first occurrence
2605
- * of the provided string.
2606
- *
2607
- * @param string $char The string to stop at.
2608
- * @return string Substring from the current document position to the first
2609
- * occurrence of the provided string.
2610
- */
2611
  protected function copy_until_char($char)
2612
  {
2613
  if ($this->char === null) { return ''; }
@@ -2627,15 +2191,6 @@ class simple_html_dom
2627
  return substr($this->doc, $pos_old, $pos - $pos_old);
2628
  }
2629
 
2630
- /**
2631
- * Remove noise from HTML content
2632
- *
2633
- * Noise is stored to {@see simple_html_dom::$noise}
2634
- *
2635
- * @param string $pattern The regex pattern used for finding noise
2636
- * @param bool $remove_tag True to remove the entire match. Default is false
2637
- * to only remove the captured data.
2638
- */
2639
  protected function remove_noise($pattern, $remove_tag = false)
2640
  {
2641
  global $debug_object;
@@ -2668,14 +2223,6 @@ class simple_html_dom
2668
  }
2669
  }
2670
 
2671
- /**
2672
- * Restore noise to HTML content
2673
- *
2674
- * Noise is restored from {@see simple_html_dom::$noise}
2675
- *
2676
- * @param string $text A subset of HTML containing noise
2677
- * @return string The same content with noise restored
2678
- */
2679
  function restore_noise($text)
2680
  {
2681
  global $debug_object;
@@ -2722,7 +2269,6 @@ class simple_html_dom
2722
  return $text;
2723
  }
2724
 
2725
- // Sometimes we NEED one of the noise elements.
2726
  function search_noise($text)
2727
  {
2728
  global $debug_object;
@@ -2756,7 +2302,6 @@ class simple_html_dom
2756
  }
2757
  }
2758
 
2759
- // camel naming conventions
2760
  function childNodes($idx = -1)
2761
  {
2762
  return $this->root->childNodes($idx);
@@ -2774,7 +2319,7 @@ class simple_html_dom
2774
 
2775
  function createElement($name, $value = null)
2776
  {
2777
- return @str_get_html("<$name>$value</$name>")->first_child();
2778
  }
2779
 
2780
  function createTextNode($value)
5
  * Website: http://sourceforge.net/projects/simplehtmldom/
6
  * Additional projects: http://sourceforge.net/projects/debugobject/
7
  * Acknowledge: Jose Solorzano (https://sourceforge.net/projects/php-html/)
 
 
 
 
8
  *
9
+ * Licensed under The MIT License
10
+ * See the LICENSE file in the project root for more information.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  *
12
+ * Authors:
13
+ * S.C. Chen
14
+ * John Schlick
15
+ * Rus Carroll
16
+ * logmanoriginal
17
  *
18
+ * Contributors:
19
+ * Yousuke Kumakura
20
+ * Vadim Voituk
21
+ * Antcs
22
  *
23
+ * Version Rev. 1.9.1 (291)
 
 
 
 
 
24
  */
25
 
 
 
 
 
26
  define('HDOM_TYPE_ELEMENT', 1);
27
  define('HDOM_TYPE_COMMENT', 2);
28
  define('HDOM_TYPE_TEXT', 3);
41
  define('HDOM_INFO_OUTER', 6);
42
  define('HDOM_INFO_ENDSPACE', 7);
43
 
 
44
  defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
 
 
45
  defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
 
 
46
  defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
 
 
47
  defined('MAX_FILE_SIZE') || define('MAX_FILE_SIZE', 600000);
 
 
48
  define('HDOM_SMARTY_AS_TEXT', 1);
49
 
 
 
 
 
50
  function file_get_html(
51
  $url,
52
  $use_include_path = false,
60
  $defaultBRText = DEFAULT_BR_TEXT,
61
  $defaultSpanText = DEFAULT_SPAN_TEXT)
62
  {
 
63
  if($maxLen <= 0) { $maxLen = MAX_FILE_SIZE; }
64
 
 
65
  $dom = new simple_html_dom(
66
  null,
67
  $lowercase,
69
  $target_charset,
70
  $stripRN,
71
  $defaultBRText,
72
+ $defaultSpanText
73
+ );
74
 
75
  /**
76
  * For sourceforge users: uncomment the next line and comment the
81
  $use_include_path,
82
  $context,
83
  $offset,
84
+ $maxLen
85
+ );
 
 
86
  // $contents = retrieve_url_contents($url);
 
87
 
88
+ if (empty($contents) || strlen($contents) > $maxLen) {
89
+ $dom->clear();
90
+ return false;
91
+ }
92
+
93
+ return $dom->load($contents, $lowercase, $stripRN);
94
  }
95
 
 
96
  function str_get_html(
97
  $str,
98
  $lowercase = true,
109
  $target_charset,
110
  $stripRN,
111
  $defaultBRText,
112
+ $defaultSpanText
113
+ );
114
 
115
  if (empty($str) || strlen($str) > MAX_FILE_SIZE) {
116
  $dom->clear();
117
  return false;
118
  }
119
 
120
+ return $dom->load($str, $lowercase, $stripRN);
 
121
  }
122
 
 
123
  function dump_html_tree($node, $show_attr = true, $deep = 0)
124
  {
125
  $node->dump($node);
126
  }
127
 
 
 
 
 
 
 
 
 
 
 
128
  class simple_html_dom_node
129
  {
 
 
 
 
 
 
 
130
  public $nodetype = HDOM_TYPE_TEXT;
 
 
 
 
 
 
 
 
131
  public $tag = 'text';
 
 
 
 
 
 
132
  public $attr = array();
 
 
 
 
 
 
133
  public $children = array();
134
  public $nodes = array();
 
 
 
 
 
 
135
  public $parent = null;
 
 
136
  public $_ = array();
 
 
 
 
 
 
137
  public $tag_start = 0;
 
 
 
 
 
 
138
  private $dom = null;
139
 
 
 
 
 
 
140
  function __construct($dom)
141
  {
142
  $this->dom = $dom;
153
  return $this->outertext();
154
  }
155
 
 
156
  function clear()
157
  {
158
  $this->dom = null;
161
  $this->children = null;
162
  }
163
 
164
+ function dump($show_attr = true, $depth = 0)
 
165
  {
166
+ echo str_repeat("\t", $depth) . $this->tag;
 
 
167
 
168
  if ($show_attr && count($this->attr) > 0) {
169
  echo '(';
170
  foreach ($this->attr as $k => $v) {
171
+ echo "[$k]=>\"$v\", ";
172
  }
173
  echo ')';
174
  }
176
  echo "\n";
177
 
178
  if ($this->nodes) {
179
+ foreach ($this->nodes as $node) {
180
+ $node->dump($show_attr, $depth + 1);
181
  }
182
  }
183
  }
184
 
 
 
185
  function dump_node($echo = true)
186
  {
187
  $string = $this->tag;
189
  if (count($this->attr) > 0) {
190
  $string .= '(';
191
  foreach ($this->attr as $k => $v) {
192
+ $string .= "[$k]=>\"$v\", ";
193
  }
194
  $string .= ')';
195
  }
200
  if (is_array($v)) {
201
  $string .= "[$k]=>(";
202
  foreach ($v as $k2 => $v2) {
203
+ $string .= "[$k2]=>\"$v2\", ";
204
  }
205
  $string .= ')';
206
  } else {
207
+ $string .= "[$k]=>\"$v\", ";
208
  }
209
  }
210
  $string .= ')';
211
  }
212
 
213
  if (isset($this->text)) {
214
+ $string .= " text: ({$this->text})";
215
  }
216
 
217
+ $string .= ' HDOM_INNER_INFO: ';
218
 
219
  if (isset($node->_[HDOM_INFO_INNER])) {
220
+ $string .= "'" . $node->_[HDOM_INFO_INNER] . "'";
221
  } else {
222
  $string .= ' NULL ';
223
  }
235
  }
236
  }
237
 
 
 
 
 
 
 
 
238
  function parent($parent = null)
239
  {
240
  // I am SURE that this doesn't work properly.
249
  return $this->parent;
250
  }
251
 
 
 
 
252
  function has_child()
253
  {
254
  return !empty($this->children);
255
  }
256
 
 
 
 
 
 
 
 
 
257
  function children($idx = -1)
258
  {
259
  if ($idx === -1) {
267
  return null;
268
  }
269
 
 
 
 
 
 
 
 
 
 
270
  function first_child()
271
  {
272
  if (count($this->children) > 0) {
275
  return null;
276
  }
277
 
 
 
 
 
 
 
 
 
278
  function last_child()
279
  {
280
+ if (count($this->children) > 0) {
281
+ return end($this->children);
282
  }
283
  return null;
284
  }
285
 
 
 
 
 
 
 
286
  function next_sibling()
287
  {
288
  if ($this->parent === null) {
289
  return null;
290
  }
291
 
292
+ $idx = array_search($this, $this->parent->children, true);
 
293
 
294
+ if ($idx !== false && isset($this->parent->children[$idx + 1])) {
295
+ return $this->parent->children[$idx + 1];
296
  }
297
 
298
+ return null;
 
 
 
 
299
  }
300
 
 
 
 
 
 
 
301
  function prev_sibling()
302
  {
303
+ if ($this->parent === null) {
304
+ return null;
305
+ }
306
 
307
+ $idx = array_search($this, $this->parent->children, true);
 
308
 
309
+ if ($idx !== false && $idx > 0) {
310
+ return $this->parent->children[$idx - 1];
311
  }
312
 
313
+ return null;
 
 
314
  }
315
 
 
 
 
 
 
 
 
 
 
 
316
  function find_ancestor_tag($tag)
317
  {
318
  global $debug_object;
319
  if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
320
 
321
+ if ($this->parent === null) {
322
+ return null;
323
+ }
324
 
325
+ $ancestor = $this->parent;
326
+
327
+ while (!is_null($ancestor)) {
328
  if (is_object($debug_object)) {
329
+ $debug_object->debug_log(2, 'Current tag is: ' . $ancestor->tag);
330
  }
331
 
332
+ if ($ancestor->tag === $tag) {
333
  break;
334
  }
335
 
336
+ $ancestor = $ancestor->parent;
337
  }
338
 
339
+ return $ancestor;
340
  }
341
 
 
 
 
 
 
342
  function innertext()
343
  {
344
  if (isset($this->_[HDOM_INFO_INNER])) {
358
  return $ret;
359
  }
360
 
 
 
 
 
 
361
  function outertext()
362
  {
363
  global $debug_object;
374
  $debug_object->debug_log(1, 'Innertext of tag: ' . $this->tag . $text);
375
  }
376
 
377
+ if ($this->tag === 'root') {
378
+ return $this->innertext();
379
+ }
380
 
381
+ // todo: What is the use of this callback? Remove?
382
  if ($this->dom && $this->dom->callback !== null) {
383
  call_user_func_array($this->dom->callback, array($this));
384
  }
391
  return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
392
  }
393
 
394
+ $ret = '';
395
+
396
  if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
397
  $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
 
 
398
  }
399
 
 
400
  if (isset($this->_[HDOM_INFO_INNER])) {
401
+ // todo: <br> should either never have HDOM_INFO_INNER or always
 
402
  if ($this->tag !== 'br') {
403
  $ret .= $this->_[HDOM_INFO_INNER];
404
  }
405
+ } elseif ($this->nodes) {
406
+ foreach ($this->nodes as $n) {
407
+ $ret .= $this->convert_text($n->outertext());
 
 
408
  }
409
  }
410
 
 
411
  if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
412
  $ret .= '</' . $this->tag . '>';
413
  }
415
  return $ret;
416
  }
417
 
 
 
 
 
 
418
  function text()
419
  {
420
  if (isset($this->_[HDOM_INFO_INNER])) {
441
  foreach ($this->nodes as $n) {
442
  // Start paragraph after a blank line
443
  if ($n->tag === 'p') {
444
+ $ret = trim($ret) . "\n\n";
445
  }
446
 
447
  $ret .= $this->convert_text($n->text());
454
  }
455
  }
456
  }
457
+ return $ret;
458
  }
459
 
 
 
 
 
 
460
  function xmltext()
461
  {
462
  $ret = $this->innertext();
465
  return $ret;
466
  }
467
 
 
468
  function makeup()
469
  {
470
  // text, comment, unknown
508
  return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
509
  }
510
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  function find($selector, $idx = null, $lowercase = false)
512
  {
513
  $selectors = $this->parse_selector($selector);
560
  return (isset($found[$idx])) ? $found[$idx] : null;
561
  }
562
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  protected function seek($selector, &$ret, $parent_cmd, $lowercase = false)
564
  {
565
  global $debug_object;
591
  && $this->parent
592
  && in_array($this, $this->parent->children)) { // Next-Sibling Combinator
593
  $index = array_search($this, $this->parent->children, true) + 1;
594
+ if ($index < count($this->parent->children))
595
+ $nodes[] = $this->parent->children[$index];
596
  } elseif ($parent_cmd === '~'
597
  && $this->parent
598
  && in_array($this, $this->parent->children)) { // Subsequent Sibling Combinator
611
  $pass = false;
612
  }
613
 
614
+ // Handle 'text' selector
615
+ if($pass && $tag === 'text' && $node->tag === 'text') {
616
+ $ret[array_search($node, $this->dom->nodes, true)] = 1;
617
+ unset($node);
618
+ continue;
619
+ }
620
+
621
  // Skip if node isn't a child node (i.e. text nodes)
622
  if($pass && !in_array($node, $node->parent->children, true)) {
623
  $pass = false;
782
  }
783
  }
784
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
785
  protected function match($exp, $pattern, $value, $case_sensitivity)
786
  {
787
  global $debug_object;
827
  return false;
828
  }
829
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
830
  protected function parse_selector($selector_string)
831
  {
832
  global $debug_object;
918
  */
919
  if($m[4] !== '') {
920
  preg_match_all(
921
+ "/\[@?(!?[\w:-]+)(?:([!*^$|~]?=)[\"']?(.*?)[\"']?)?(?:\s+?([iIsS])?)?\]/is",
922
  trim($m[4]),
923
  $attributes,
924
  PREG_SET_ORDER
1018
  if (isset($this->attr[$name])) { unset($this->attr[$name]); }
1019
  }
1020
 
 
 
1021
  function convert_text($text)
1022
  {
1023
  global $debug_object;
1068
  return $converted_text;
1069
  }
1070
 
 
 
 
 
 
 
1071
  static function is_utf8($str)
1072
  {
1073
  $c = 0; $b = 0;
1095
  return true;
1096
  }
1097
 
 
 
 
 
 
 
 
 
 
 
1098
  function get_display_size()
1099
  {
1100
  global $debug_object;
1180
  return $result;
1181
  }
1182
 
1183
+ function save($filepath = '')
1184
+ {
1185
+ $ret = $this->outertext();
1186
+
1187
+ if ($filepath !== '') {
1188
+ file_put_contents($filepath, $ret, LOCK_EX);
1189
+ }
1190
+
1191
+ return $ret;
1192
+ }
1193
+
1194
+ function addClass($class)
1195
+ {
1196
+ if (is_string($class)) {
1197
+ $class = explode(' ', $class);
1198
+ }
1199
+
1200
+ if (is_array($class)) {
1201
+ foreach($class as $c) {
1202
+ if (isset($this->class)) {
1203
+ if ($this->hasClass($c)) {
1204
+ continue;
1205
+ } else {
1206
+ $this->class .= ' ' . $c;
1207
+ }
1208
+ } else {
1209
+ $this->class = $c;
1210
+ }
1211
+ }
1212
+ } else {
1213
+ if (is_object($debug_object)) {
1214
+ $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
1215
+ }
1216
+ }
1217
+ }
1218
+
1219
+ function hasClass($class)
1220
+ {
1221
+ if (is_string($class)) {
1222
+ if (isset($this->class)) {
1223
+ return in_array($class, explode(' ', $this->class), true);
1224
+ }
1225
+ } else {
1226
+ if (is_object($debug_object)) {
1227
+ $debug_object->debug_log(2, 'Invalid type: ', gettype($class));
1228
+ }
1229
+ }
1230
+
1231
+ return false;
1232
+ }
1233
+
1234
+ function removeClass($class = null)
1235
+ {
1236
+ if (!isset($this->class)) {
1237
+ return;
1238
+ }
1239
+
1240
+ if (is_null($class)) {
1241
+ $this->removeAttribute('class');
1242
+ return;
1243
+ }
1244
+
1245
+ if (is_string($class)) {
1246
+ $class = explode(' ', $class);
1247
+ }
1248
+
1249
+ if (is_array($class)) {
1250
+ $class = array_diff(explode(' ', $this->class), $class);
1251
+ if (empty($class)) {
1252
+ $this->removeAttribute('class');
1253
+ } else {
1254
+ $this->class = implode(' ', $class);
1255
+ }
1256
+ }
1257
+ }
1258
+
1259
  function getAllAttributes()
1260
  {
1261
  return $this->attr;
1281
  $this->__set($name, null);
1282
  }
1283
 
1284
+ function remove()
1285
+ {
1286
+ if ($this->parent) {
1287
+ $this->parent->removeChild($this);
1288
+ }
1289
+ }
1290
+
1291
+ function removeChild($node)
1292
+ {
1293
+ $nidx = array_search($node, $this->nodes, true);
1294
+ $cidx = array_search($node, $this->children, true);
1295
+ $didx = array_search($node, $this->dom->nodes, true);
1296
+
1297
+ if ($nidx !== false && $cidx !== false && $didx !== false) {
1298
+
1299
+ foreach($node->children as $child) {
1300
+ $node->removeChild($child);
1301
+ }
1302
+
1303
+ foreach($node->nodes as $entity) {
1304
+ $enidx = array_search($entity, $node->nodes, true);
1305
+ $edidx = array_search($entity, $node->dom->nodes, true);
1306
+
1307
+ if ($enidx !== false && $edidx !== false) {
1308
+ unset($node->nodes[$enidx]);
1309
+ unset($node->dom->nodes[$edidx]);
1310
+ }
1311
+ }
1312
+
1313
+ unset($this->nodes[$nidx]);
1314
+ unset($this->children[$cidx]);
1315
+ unset($this->dom->nodes[$didx]);
1316
+
1317
+ $node->clear();
1318
+
1319
+ }
1320
+ }
1321
+
1322
  function getElementById($id)
1323
  {
1324
  return $this->find("#$id", 0);
1387
 
1388
  }
1389
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1390
  class simple_html_dom
1391
  {
 
 
 
 
 
1392
  public $root = null;
 
 
 
 
 
 
1393
  public $nodes = array();
 
 
 
 
 
 
1394
  public $callback = null;
 
 
 
 
 
 
 
1395
  public $lowercase = false;
 
 
 
 
 
 
 
 
1396
  public $original_size;
 
 
 
 
 
 
 
 
 
 
 
1397
  public $size;
1398
 
 
 
 
 
 
1399
  protected $pos;
 
 
 
 
 
 
1400
  protected $doc;
 
 
 
 
 
 
 
 
 
 
 
 
1401
  protected $char;
1402
 
1403
  protected $cursor;
 
 
 
 
 
 
1404
  protected $parent;
1405
  protected $noise = array();
 
 
 
 
 
 
1406
  protected $token_blank = " \t\r\n";
 
 
 
 
 
 
 
 
1407
  protected $token_equal = ' =/>';
 
 
 
 
 
 
 
1408
  protected $token_slash = " />\r\n\t";
 
 
 
 
 
 
1409
  protected $token_attr = ' >';
1410
 
 
 
1411
  public $_charset = '';
1412
  public $_target_charset = '';
1413
 
 
 
 
 
 
1414
  protected $default_br_text = '';
1415
 
 
 
 
 
 
1416
  public $default_span_text = '';
1417
 
 
 
 
 
 
 
 
 
 
 
 
 
1418
  protected $self_closing_tags = array(
1419
  'area' => 1,
1420
  'base' => 1,
1431
  'track' => 1,
1432
  'wbr' => 1
1433
  );
 
 
 
 
 
 
 
 
 
 
 
 
1434
  protected $block_tags = array(
1435
  'body' => 1,
1436
  'div' => 1,
1439
  'span' => 1,
1440
  'table' => 1
1441
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1442
  protected $optional_closing_tags = array(
1443
  // Not optional, see
1444
  // https://www.w3.org/TR/html/textlevel-semantics.html#the-b-element
1497
  $this->clear();
1498
  }
1499
 
 
1500
  function load(
1501
  $str,
1502
  $lowercase = true,
1551
  return $this;
1552
  }
1553
 
 
1554
  function load_file()
1555
  {
1556
  $args = func_get_args();
1562
  }
1563
  }
1564
 
 
 
 
 
 
 
 
1565
  function set_callback($function_name)
1566
  {
1567
  $this->callback = $function_name;
1568
  }
1569
 
 
 
 
 
 
1570
  function remove_callback()
1571
  {
1572
  $this->callback = null;
1573
  }
1574
 
 
1575
  function save($filepath = '')
1576
  {
1577
  $ret = $this->root->innertext();
1579
  return $ret;
1580
  }
1581
 
 
 
1582
  function find($selector, $idx = null, $lowercase = false)
1583
  {
1584
  return $this->root->find($selector, $idx, $lowercase);
1585
  }
1586
 
 
1587
  function clear()
1588
  {
1589
+ if (isset($this->nodes)) {
1590
+ foreach ($this->nodes as $n) {
1591
+ $n->clear();
1592
+ $n = null;
1593
+ }
1594
  }
1595
 
1596
  // This add next line is documented in the sourceforge repository.
1598
  // use of clear.
1599
  if (isset($this->children)) {
1600
  foreach ($this->children as $n) {
1601
+ $n->clear();
1602
+ $n = null;
1603
  }
1604
  }
1605
 
1622
  $this->root->dump($show_attr);
1623
  }
1624
 
 
1625
  protected function prepare(
1626
  $str, $lowercase = true,
1627
  $defaultBRText = DEFAULT_BR_TEXT,
1647
  if ($this->size > 0) { $this->char = $this->doc[0]; }
1648
  }
1649
 
 
 
 
 
 
1650
  protected function parse()
1651
  {
1652
  while (true) {
1668
  }
1669
  }
1670
 
 
 
 
 
 
 
 
1671
  protected function parse_charset()
1672
  {
1673
  global $debug_object;
1689
  }
1690
 
1691
  if (empty($charset)) {
1692
+ // https://www.w3.org/TR/html/document-metadata.html#statedef-http-equiv-content-type
1693
  $el = $this->root->find('meta[http-equiv=Content-Type]', 0, true);
1694
 
1695
  if (!empty($el)) {
1726
  }
1727
  }
1728
 
 
 
1729
  if (empty($charset)) {
1730
+ // https://www.w3.org/TR/html/document-metadata.html#character-encoding-declaration
1731
+ if ($meta = $this->root->find('meta[charset]', 0)) {
1732
+ $charset = $meta->charset;
1733
+ if (is_object($debug_object)) {
1734
+ $debug_object->debug_log(2, 'meta charset: ' . $charset);
1735
+ }
1736
+ }
1737
+ }
1738
+
1739
+ if (empty($charset)) {
1740
+ // Try to guess the charset based on the content
1741
+ // Requires Multibyte String (mbstring) support (optional)
1742
  if (function_exists('mb_detect_encoding')) {
1743
+ /**
1744
+ * mb_detect_encoding() is not intended to distinguish between
1745
+ * charsets, especially single-byte charsets. Its primary
1746
+ * purpose is to detect which multibyte encoding is in use,
1747
+ * i.e. UTF-8, UTF-16, shift-JIS, etc.
1748
+ *
1749
+ * -- https://bugs.php.net/bug.php?id=38138
1750
+ *
1751
+ * Adding both CP1251/ISO-8859-5 and CP1252/ISO-8859-1 will
1752
+ * always result in CP1251/ISO-8859-5 and vice versa.
1753
+ *
1754
+ * Thus, only detect if it's either UTF-8 or CP1252/ISO-8859-1
1755
+ * to stay compatible.
1756
+ */
1757
+ $encoding = mb_detect_encoding(
1758
+ $this->doc,
1759
+ array( 'UTF-8', 'CP1252', 'ISO-8859-1' )
1760
  );
1761
 
1762
+ if ($encoding === 'CP1252' || $encoding === 'ISO-8859-1') {
1763
+ // Due to a limitation of mb_detect_encoding
1764
+ // 'CP1251'/'ISO-8859-5' will be detected as
1765
+ // 'CP1252'/'ISO-8859-1'. This will cause iconv to fail, in
1766
+ // which case we can simply assume it is the other charset.
1767
+ if (!@iconv('CP1252', 'UTF-8', $this->doc)) {
1768
+ $encoding = 'CP1251';
1769
+ }
1770
  }
 
1771
 
1772
+ if ($encoding !== false) {
1773
+ $charset = $encoding;
1774
+ if (is_object($debug_object)) {
1775
+ $debug_object->debug_log(2, 'mb_detect: ' . $charset);
1776
+ }
 
 
 
 
1777
  }
1778
+ }
1779
+ }
1780
 
1781
+ if (empty($charset)) {
1782
+ // Assume it's UTF-8 as it is the most likely charset to be used
1783
+ $charset = 'UTF-8';
1784
+ if (is_object($debug_object)) {
1785
+ $debug_object->debug_log(2, 'No match found, assume ' . $charset);
1786
  }
1787
  }
1788
 
1789
  // Since CP1252 is a superset, if we get one of it's subsets, we want
1790
  // it instead.
1791
+ if ((strtolower($charset) == 'iso-8859-1')
1792
+ || (strtolower($charset) == 'latin1')
1793
+ || (strtolower($charset) == 'latin-1')) {
1794
+ $charset = 'CP1252';
1795
  if (is_object($debug_object)) {
1796
+ $debug_object->debug_log(2,
 
1797
  'replacing ' . $charset . ' with CP1252 as its a superset'
1798
  );
1799
  }
 
 
1800
  }
1801
 
1802
  if (is_object($debug_object)) {
1806
  return $this->_charset = $charset;
1807
  }
1808
 
 
 
 
 
 
1809
  protected function read_tag()
1810
  {
1811
  // Set end position if no further tags found
2084
  return true;
2085
  }
2086
 
 
 
 
 
 
 
 
 
2087
  protected function parse_attr($node, $name, &$space)
2088
  {
2089
+ $is_duplicate = isset($node->attr[$name]);
 
 
 
 
 
 
2090
 
2091
+ if (!$is_duplicate) // Copy whitespace between "=" and value
2092
+ $space[2] = $this->copy_skip($this->token_blank);
2093
 
2094
  switch ($this->char) {
2095
+ case '"':
2096
+ $quote_type = HDOM_QUOTE_DOUBLE;
2097
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2098
+ $value = $this->copy_until_char('"');
2099
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2100
  break;
2101
+ case '\'':
2102
+ $quote_type = HDOM_QUOTE_SINGLE;
2103
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2104
+ $value = $this->copy_until_char('\'');
2105
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2106
  break;
2107
+ default:
2108
+ $quote_type = HDOM_QUOTE_NO;
2109
+ $value = $this->copy_until($this->token_attr);
2110
  }
2111
+
2112
+ $value = $this->restore_noise($value);
2113
+
2114
  // PaperG: Attributes should not have \r or \n in them, that counts as
2115
  // html whitespace.
2116
+ $value = str_replace("\r", '', $value);
2117
+ $value = str_replace("\n", '', $value);
2118
+
2119
  // PaperG: If this is a "class" selector, lets get rid of the preceeding
2120
  // and trailing space since some people leave it in the multi class case.
2121
  if ($name === 'class') {
2122
+ $value = trim($value);
2123
+ }
2124
+
2125
+ if (!$is_duplicate) {
2126
+ $node->_[HDOM_INFO_QUOTE][] = $quote_type;
2127
+ $node->attr[$name] = $value;
2128
  }
2129
  }
2130
 
 
 
 
 
 
 
 
 
2131
  protected function link_nodes(&$node, $is_child)
2132
  {
2133
  $node->parent = $this->parent;
2137
  }
2138
  }
2139
 
 
 
 
 
 
 
2140
  protected function as_text_node($tag)
2141
  {
2142
  $node = new simple_html_dom_node($this);
2147
  return true;
2148
  }
2149
 
 
 
 
 
 
 
 
 
2150
  protected function skip($chars)
2151
  {
2152
  $this->pos += strspn($this->doc, $chars, $this->pos);
2153
  $this->char = ($this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2154
  }
2155
 
 
 
 
 
 
 
 
 
2156
  protected function copy_skip($chars)
2157
  {
2158
  $pos = $this->pos;
2163
  return substr($this->doc, $pos, $len);
2164
  }
2165
 
 
 
 
 
 
 
 
 
2166
  protected function copy_until($chars)
2167
  {
2168
  $pos = $this->pos;
2172
  return substr($this->doc, $pos, $len);
2173
  }
2174
 
 
 
 
 
 
 
 
 
2175
  protected function copy_until_char($char)
2176
  {
2177
  if ($this->char === null) { return ''; }
2191
  return substr($this->doc, $pos_old, $pos - $pos_old);
2192
  }
2193
 
 
 
 
 
 
 
 
 
 
2194
  protected function remove_noise($pattern, $remove_tag = false)
2195
  {
2196
  global $debug_object;
2223
  }
2224
  }
2225
 
 
 
 
 
 
 
 
 
2226
  function restore_noise($text)
2227
  {
2228
  global $debug_object;
2269
  return $text;
2270
  }
2271
 
 
2272
  function search_noise($text)
2273
  {
2274
  global $debug_object;
2302
  }
2303
  }
2304
 
 
2305
  function childNodes($idx = -1)
2306
  {
2307
  return $this->root->childNodes($idx);
2319
 
2320
  function createElement($name, $value = null)
2321
  {
2322
+ return @str_get_html("<$name>$value</$name>")->firstChild();
2323
  }
2324
 
2325
  function createTextNode($value)