From 60a2fcd9a8a9dfabdb614048b3c06e98d5c55c09 Mon Sep 17 00:00:00 2001
From: Tony Bogdanov
- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018.
+ Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.
- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018.
+ Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.
-
-
+
+ CRAP
-
-
+
+
@@ -80,22 +80,22 @@
-
-
- 1276.44
+
+
+ 503.17
-
-
+
+
@@ -535,7 +535,28 @@
-
+
+ find
+ wrapInner
+
+
+
+ 2
+
+
+
+
+
find
-
addClass
-
-
removeClass
-
-
hasClass
-
-
attr
+ attr
-
removeAttr
+ removeAttr
-
text
+ text
-
html
+ html
Legend
High: 90% to 100%
Legend
Dead Code
- 16
+ 17
-
+ matchAttributeNode
+ matchAttributeNode
+
+
@@ -130,118 +141,127 @@
match
+
+
+
+ 1
+
+
+
+ namespace SDom\SelectorMatcher;
- use SDom\Node\Element;
- use SDom\SelectorMatcher;
- use Symfony\Component\CssSelector\Node\AttributeNode;
-
- /**
- * @pattern E[foo]
- * @meaning an E element with a "foo" attribute
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo="bar"]
- * @meaning an E element whose "foo" attribute value is exactly equal to "bar"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo~="bar"]
- * @meaning an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is
- * exactly equal to "bar"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo^="bar"]
- * @meaning an E element whose "foo" attribute value begins exactly with the string "bar"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo$="bar"]
- * @meaning an E element whose "foo" attribute value ends exactly with the string "bar"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo*="bar"]
- * @meaning an E element whose "foo" attribute value contains the substring "bar"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * @pattern E[foo|="en"]
- * @meaning an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en"
- * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
- *
- * Trait AttributeNodeTrait
- * @package SDom\SelectorMatcher
- */
- trait AttributeNodeTrait
- {
- /**
- * @param AttributeNode $token
- * @param Element $node
- * @param null|Element $effectiveRoot
- * @return bool
- */
- protected function matchAttributeNode(AttributeNode $token, Element $node, Element $effectiveRoot = null): bool
- {
- $attribute = $token->getAttribute();
-
- // node attribute must exist, regardless of operator
- if (!$node->hasAttribute($attribute)) {
- return false;
- }
-
- $neededValue = $token->getValue();
- $actualValue = $node->getAttribute($attribute);
- $operator = $token->getOperator();
-
- // if attribute operator is "exists", no further checks are needed
- if ('exists' === $operator) {
- return $this->match($token->getSelector(), $node, $effectiveRoot);
- }
-
- switch ($token->getOperator()) {
- case '=':
- if ($neededValue !== $actualValue) {
- return false;
- }
- break;
-
- case '~=':
- if (!SelectorMatcher::containsWord($neededValue, $actualValue)) {
- return false;
- }
- break;
-
- case '^=':
- if ($neededValue !== substr($actualValue, 0, strlen($neededValue))) {
- return false;
- }
- break;
-
- case '$=':
- if ($neededValue !== substr($actualValue, -strlen($neededValue))) {
- return false;
- }
- break;
-
- case '*=':
- if (false === strpos($actualValue, $neededValue)) {
- return false;
- }
- break;
-
- case '|=':
- if (
- $neededValue !== $actualValue &&
- $neededValue . '-' !== substr($actualValue, 0, strlen($neededValue . '-'))
- ) {
- return false;
- }
- break;
-
- default:
- throw new \RuntimeException(sprintf(
- 'Invalid node attribute operator "%s".',
- $token->getOperator()
- ));
- }
-
- return $this->match($token->getSelector(), $node, $effectiveRoot);
- }
+ }
+ use SDom\Node\NodeInterface;
+ use SDom\SelectorMatcher;
+ use Symfony\Component\CssSelector\Node\AttributeNode;
+
+ /**
+ * @pattern E[foo]
+ * @meaning an E element with a "foo" attribute
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo="bar"]
+ * @meaning an E element whose "foo" attribute value is exactly equal to "bar"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo~="bar"]
+ * @meaning an E element whose "foo" attribute value is a list of whitespace-separated values, one of which is
+ * exactly equal to "bar"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo^="bar"]
+ * @meaning an E element whose "foo" attribute value begins exactly with the string "bar"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo$="bar"]
+ * @meaning an E element whose "foo" attribute value ends exactly with the string "bar"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo*="bar"]
+ * @meaning an E element whose "foo" attribute value contains the substring "bar"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * @pattern E[foo|="en"]
+ * @meaning an E element whose "foo" attribute has a hyphen-separated list of values beginning (from the left) with "en"
+ * @link https://www.w3.org/TR/css3-selectors/#attribute-selectors
+ *
+ * Trait AttributeNodeTrait
+ * @package SDom\SelectorMatcher
+ */
+ trait AttributeNodeTrait
+ {
+ /**
+ * @param AttributeNode $token
+ * @param Element $node
+ * @param null|Element $effectiveRoot
+ * @return bool
+ */
+ protected function matchAttributeNode(AttributeNode $token, Element $node, Element $effectiveRoot = null): bool
+ {
+ $attribute = $token->getAttribute();
+
+ // node attribute must exist, regardless of operator
+ if (!$node->hasAttribute($attribute)) {
+ return false;
+ }
+
+ $neededValue = $token->getValue();
+ $actualValue = $node->getAttribute($attribute);
+ $operator = $token->getOperator();
+
+ // if attribute operator is "exists", no further checks are needed
+ if ('exists' === $operator) {
+ return $this->match($token->getSelector(), $node, $effectiveRoot);
+ }
+
+ switch ($token->getOperator()) {
+ case '=':
+ if ($neededValue !== $actualValue) {
+ return false;
+ }
+ break;
+
+ case '~=':
+ if (!SelectorMatcher::containsWord($neededValue, $actualValue)) {
+ return false;
+ }
+ break;
+
+ case '^=':
+ if ($neededValue !== substr($actualValue, 0, strlen($neededValue))) {
+ return false;
+ }
+ break;
+
+ case '$=':
+ if ($neededValue !== substr($actualValue, -strlen($neededValue))) {
+ return false;
+ }
+ break;
+
+ case '*=':
+ if (false === strpos($actualValue, $neededValue)) {
+ return false;
+ }
+ break;
+
+ case '|=':
+ if (
+ $neededValue !== $actualValue &&
+ $neededValue . '-' !== substr($actualValue, 0, strlen($neededValue . '-'))
+ ) {
+ return false;
+ }
+ break;
+
+ default:
+ throw new \RuntimeException(sprintf(
+ 'Invalid node attribute operator "%s".',
+ $token->getOperator()
+ ));
+ }
+
+ return $this->match($token->getSelector(), $node, $effectiveRoot);
+ }
+
+ /**
+ * @param NodeInterface $token
+ * @param Element $node
+ * @param Element|null $effectiveRoot
+ * @return bool
+ */
+ abstract public function match(NodeInterface $token, Element $node, Element $effectiveRoot = null): bool;
@@ -254,7 +274,7 @@ } Legend
Dead Code
- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.
diff --git a/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html b/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html index ad13b19..f8820f4 100644 --- a/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html +++ b/docs/coverage/SelectorMatcher/ClassNodeTrait.php.html @@ -88,7 +88,7 @@- Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 16:56:52 EEST 2018. + Generated by php-code-coverage 5.3.0-1-g982ce79 using PHP 7.2.2 with Xdebug 2.6.0 and PHPUnit 6.5.5 at Sun Apr 1 22:08:03 EEST 2018.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887:
+ 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602: 603: 604: 605: 606: 607: 608: 609: 610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632: 633: 634: 635: 636: 637: 638: 639: 640: 641: 642: 643: 644: 645: 646: 647: 648: 649: 650: 651: 652: 653: 654: 655: 656: 657: 658: 659: 660: 661: 662: 663: 664: 665: 666: 667: 668: 669: 670: 671: 672: 673: 674: 675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718: 719: 720: 721: 722: 723: 724: 725: 726: 727: 728: 729: 730: 731: 732: 733: 734: 735: 736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842: 843: 844: 845: 846: 847: 848: 849: 850: 851: 852: 853: 854: 855: 856: 857: 858: 859: 860: 861: 862: 863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 888: 889: 890: 891: 892: 893: 894: 895: 896: 897: 898: 899: 900: 901: 902: 903: 904: 905: 906: 907: 908:
<?php
namespace SDom;
@@ -696,266 +696,287 @@
}
/**
- * Return a new Dom collection of all the descendants of each Element node in the current collection,
- * filtered by the specified CSS selector.
- *
- * @param string $selector
- * @return Dom
- */
- public function find(string $selector): Dom
- {
- if (!isset(self::$selectorParser)) {
- self::$selectorParser = new Parser();
- }
-
- if (!isset(self::$selectorMatcher)) {
- self::$selectorMatcher = new SelectorMatcher();
- }
-
- $dom = new static();
- $selectorTokens = self::$selectorParser->parse($selector);
-
- foreach ($this->get() as $rootNode) {
- /** @var NodeInterface $childNode */
- foreach ($rootNode as $childNode) {
- self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode);
- }
- }
-
- return $dom;
- }
-
- /**
- * Adds the specified class(es) to each Element node in the collection.
- *
- * @param string $className
- * @return Dom
- */
- public function addClass(string $className): Dom
- {
- $addClasses = preg_split('/\s+/', $className);
-
- // bail if no classes to add
- if (0 === count($addClasses)) {
- return $this;
+ * Wrap a clone of the supplied content around each child node (not only element nodes) of nodes in the collection.
+ *
+ * This function works exactly like wrap() except it wraps the children of the nodes in the collection instead.
+ *
+ * Return the original collection for chaining.
+ *
+ * @param $content
+ * @return Dom
+ */
+ public function wrapInner($content): Dom
+ {
+ foreach ($this->children() as $child) {
+ $child->wrap($content);
+ }
+ return $this;
+ }
+
+ /**
+ * Return a new Dom collection of all the descendants of each Element node in the current collection,
+ * filtered by the specified CSS selector.
+ *
+ * @param string $selector
+ * @return Dom
+ */
+ public function find(string $selector): Dom
+ {
+ if (!isset(self::$selectorParser)) {
+ self::$selectorParser = new Parser();
+ }
+
+ if (!isset(self::$selectorMatcher)) {
+ self::$selectorMatcher = new SelectorMatcher();
+ }
+
+ $dom = new static();
+ $selectorTokens = self::$selectorParser->parse($selector);
+
+ foreach ($this->get() as $rootNode) {
+ /** @var NodeInterface $childNode */
+ foreach ($rootNode as $childNode) {
+ self::traverseMatch($dom, $selectorTokens, $childNode, $rootNode);
+ }
}
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- // if the node already has a "class" attribute, merge all classes & make sure the result is unique
- if ($node->hasAttribute('class')) {
- $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
- $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses))));
- }
-
- // if the node does not have a "class" attribute, directly set the new ones
- else {
- $node->setAttribute('class', implode(' ', $addClasses));
- }
+ return $dom;
+ }
+
+ /**
+ * Adds the specified class(es) to each Element node in the collection.
+ *
+ * @param string $className
+ * @return Dom
+ */
+ public function addClass(string $className): Dom
+ {
+ $className = trim($className);
+
+ // bail if no classes to add
+ if ('' === $className) {
+ return $this;
}
- return $this;
- }
-
- /**
- * Remove a single class, multiple classes, or all classes from each Element node in the collection.
- *
- * @param string|null $className
- * @return Dom
- */
- public function removeClass(string $className = null): Dom
- {
- // if class to remove isn't set, remove all classes, but keep the "class" attribute present
- if (!isset($className)) {
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element || !$node->hasAttribute('class')) {
- continue;
- }
-
- $node->setAttribute('class', '');
- }
-
- return $this;
- }
-
- $removeClasses = preg_split('/\s+/', $className);
-
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element || !$node->hasAttribute('class')) {
- continue;
- }
-
- // set to the difference between the current classes and the remove ones
- $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
- $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses)));
- }
-
- return $this;
- }
-
- /**
- * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned.
- *
- * @param string $className
- * @return bool
- */
- public function hasClass(string $className): bool
- {
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element || !$node->hasAttribute('class')) {
- continue;
- }
-
- if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Get the value of an attribute for the first Element node in the collection.
- * Set the value of an attribute for each Element node in the collection.
- *
- * @param string $name
- * @param string|null $value
- * @return string|null|$this
- */
- public function attr(string $name, string $value = null)
- {
- if (isset($value)) {
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- $node->setAttribute($name, $value);
- }
-
- return $this;
- }
-
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- return $node->getAttribute($name);
- }
-
- return null;
- }
-
- /**
- * Remove an attribute from each Element node in the collection.
- *
- * @param string $name
- * @return Dom
- */
- public function removeAttr(string $name): Dom
- {
- /** @var NodeInterface $node */
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- $node->removeAttribute($name);
- }
-
- return $this;
- }
-
- /**
- * Get the combined text contents of each Element node in the collection, including their descendants.
- * Set the content of each Element node in the collection to the specified text.
+ $addClasses = preg_split('/\s+/', $className);
+
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ // if the node already has a "class" attribute, merge all classes & make sure the result is unique
+ if ($node->hasAttribute('class')) {
+ $currentClassName = trim($node->getAttribute('class'));
+ $currentClasses = '' === $currentClassName ? [] : preg_split('/\s+/', $currentClassName);
+ $node->setAttribute('class', implode(' ', array_unique(array_merge($currentClasses, $addClasses))));
+ }
+
+ // if the node does not have a "class" attribute, directly set the new ones
+ else {
+ $node->setAttribute('class', implode(' ', $addClasses));
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Remove a single class, multiple classes, or all classes from each Element node in the collection.
+ *
+ * @param string|null $className
+ * @return Dom
+ */
+ public function removeClass(string $className = null): Dom
+ {
+ // if class to remove isn't set, remove all classes, but keep the "class" attribute present
+ if (!isset($className)) {
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element || !$node->hasAttribute('class')) {
+ continue;
+ }
+
+ $node->setAttribute('class', '');
+ }
+
+ return $this;
+ }
+
+ $removeClasses = preg_split('/\s+/', $className);
+
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element || !$node->hasAttribute('class')) {
+ continue;
+ }
+
+ // set to the difference between the current classes and the remove ones
+ $currentClasses = preg_split('/\s+/', $node->getAttribute('class'));
+ $node->setAttribute('class', implode(' ', array_diff($currentClasses, $removeClasses)));
+ }
+
+ return $this;
+ }
+
+ /**
+ * Return TRUE if at least one of the Element nodes in the collection has the specified class assigned.
+ *
+ * @param string $className
+ * @return bool
+ */
+ public function hasClass(string $className): bool
+ {
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element || !$node->hasAttribute('class')) {
+ continue;
+ }
+
+ if (SelectorMatcher::containsWord($className, $node->getAttribute('class'))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the value of an attribute for the first Element node in the collection.
+ * Set the value of an attribute for each Element node in the collection.
+ *
+ * @param string $name
+ * @param string|null $value
+ * @return string|null|$this
+ */
+ public function attr(string $name, string $value = null)
+ {
+ if (isset($value)) {
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ $node->setAttribute($name, $value);
+ }
+
+ return $this;
+ }
+
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ return $node->getAttribute($name);
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove an attribute from each Element node in the collection.
*
- * @param string|null $text
- * @return $this|string
+ * @param string $name
+ * @return Dom
*/
- public function text(string $text = null)
+ public function removeAttr(string $name): Dom
{
- if (isset($text)) {
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
+ /** @var NodeInterface $node */
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
- $node->clear()->insertAfter(new Text($text));
- }
+ $node->removeAttribute($name);
+ }
- return $this;
- }
+ return $this;
+ }
- $text = '';
-
- foreach ($this->nodes as $node) {
- if ($node instanceof Text) {
- $text .= (string) $node;
- } else if ($node instanceof Element) {
- /** @var Dom $dom */
- foreach ((new static($node))->children() as $dom) {
- $text .= $dom->text();
- }
- }
- }
-
- return $text;
- }
-
- /**
- * Get the HTML contents of the first Element node in the collection.
- * Set the HTML contents of each Element node in the collection.
- *
- * @param string|null $html
- * @return $this|string
- */
- public function html(string $html = null)
- {
- if (isset($html)) {
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- $node->clear();
-
- foreach ((new static($html))->nodes as $newNode) {
- $node->insertAfter($newNode);
- }
- }
-
- return $this;
- }
-
- foreach ($this->nodes as $node) {
- if (!$node instanceof Element) {
- continue;
- }
-
- $html = '';
-
- /** @var NodeInterface $childNode */
- foreach ($node as $childNode) {
- $html .= (string) $childNode;
- }
-
- return $html;
- }
-
- return '';
- }
-}
+ /**
+ * Get the combined text contents of each Element node in the collection, including their descendants.
+ * Set the content of each Element node in the collection to the specified text.
+ *
+ * @param string|null $text
+ * @return $this|string
+ */
+ public function text(string $text = null)
+ {
+ if (isset($text)) {
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ $node->clear()->insertAfter(new Text($text));
+ }
+
+ return $this;
+ }
+
+ $text = '';
+
+ foreach ($this->nodes as $node) {
+ if ($node instanceof Text) {
+ $text .= (string) $node;
+ } else if ($node instanceof Element) {
+ /** @var Dom $dom */
+ foreach ((new static($node))->children() as $dom) {
+ $text .= $dom->text();
+ }
+ }
+ }
+
+ return $text;
+ }
+
+ /**
+ * Get the HTML contents of the first Element node in the collection.
+ * Set the HTML contents of each Element node in the collection.
+ *
+ * @param string|null $html
+ * @return $this|string
+ */
+ public function html(string $html = null)
+ {
+ if (isset($html)) {
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ $node->clear();
+
+ foreach ((new static($html))->nodes as $newNode) {
+ $node->insertAfter($newNode);
+ }
+ }
+
+ return $this;
+ }
+
+ foreach ($this->nodes as $node) {
+ if (!$node instanceof Element) {
+ continue;
+ }
+
+ $html = '';
+
+ /** @var NodeInterface $childNode */
+ foreach ($node as $childNode) {
+ $html .= (string) $childNode;
+ }
+
+ return $html;
+ }
+
+ return '';
+ }
+}