Skip to content

Commit 7e5bf94

Browse files
Merge pull request #203 from vinkius-labs/develop
Develop
2 parents ada1ee0 + d86e0db commit 7e5bf94

13 files changed

+344
-79
lines changed

.github/workflows/code-quality.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717

1818
steps:
1919
- name: Checkout code
20-
uses: actions/checkout@v4
20+
uses: actions/checkout@v5
2121

2222
- name: Setup PHP
2323
uses: shivammathur/setup-php@v2

.github/workflows/pull-request.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818

1919
steps:
2020
- name: Checkout code
21-
uses: actions/checkout@v4
21+
uses: actions/checkout@v5
2222
with:
2323
fetch-depth: 0
2424

@@ -34,7 +34,7 @@ jobs:
3434

3535
- name: Cache Composer packages
3636
id: composer-cache
37-
uses: actions/cache@v3
37+
uses: actions/cache@v4
3838
with:
3939
path: vendor
4040
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
@@ -66,7 +66,7 @@ jobs:
6666
echo "✅ Coverage is acceptable ($COVERAGE%)"
6767
6868
- name: Comment PR with results
69-
uses: actions/github-script@v7
69+
uses: actions/github-script@v8
7070
if: always()
7171
with:
7272
script: |

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131

3232
steps:
3333
- name: Checkout code
34-
uses: actions/checkout@v4
34+
uses: actions/checkout@v5
3535

3636
- name: Setup PHP
3737
uses: shivammathur/setup-php@v2

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
},
2222
"require-dev": {
2323
"phpunit/phpunit": "^10.5 || ^11.0",
24-
"orchestra/testbench": "^8.0 || ^9.0 || ^10.0",
24+
"orchestra/testbench": "^10.6.0",
2525
"squizlabs/php_codesniffer": "^3.6",
2626
"mockery/mockery": "^1.6"
2727
},

src/Middleware/InsertDNSPrefetch.php

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,76 @@ class InsertDNSPrefetch extends PageSpeed
66
{
77
public function apply($buffer)
88
{
9+
// Extract URLs only from HTML attributes, not from script/style content
10+
$urls = [];
11+
12+
// Step 1: Extract URLs from script src/href attributes
913
preg_match_all(
10-
'#\bhttps?://[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/))#',
14+
'#<script[^>]+src=["\']([^"\']+)["\']#i',
1115
$buffer,
12-
$matches,
13-
PREG_OFFSET_CAPTURE
16+
$scriptMatches
1417
);
18+
if (!empty($scriptMatches[1])) {
19+
$urls = array_merge($urls, $scriptMatches[1]);
20+
}
21+
22+
// Step 2: Extract URLs from link href attributes
23+
preg_match_all(
24+
'#<link[^>]+href=["\']([^"\']+)["\']#i',
25+
$buffer,
26+
$linkMatches
27+
);
28+
if (!empty($linkMatches[1])) {
29+
$urls = array_merge($urls, $linkMatches[1]);
30+
}
31+
32+
// Step 3: Extract URLs from img src attributes
33+
preg_match_all(
34+
'#<img[^>]+src=["\']?([^"\'\s>]+)["\']?#i',
35+
$buffer,
36+
$imgMatches
37+
);
38+
if (!empty($imgMatches[1])) {
39+
$urls = array_merge($urls, $imgMatches[1]);
40+
}
41+
42+
// Step 4: Extract URLs from anchor href attributes
43+
preg_match_all(
44+
'#<a[^>]+href=["\']([^"\']+)["\']#i',
45+
$buffer,
46+
$anchorMatches
47+
);
48+
if (!empty($anchorMatches[1])) {
49+
$urls = array_merge($urls, $anchorMatches[1]);
50+
}
51+
52+
// Step 5: Extract URLs from iframe src attributes
53+
preg_match_all(
54+
'#<iframe[^>]+src=["\']([^"\']+)["\']#i',
55+
$buffer,
56+
$iframeMatches
57+
);
58+
if (!empty($iframeMatches[1])) {
59+
$urls = array_merge($urls, $iframeMatches[1]);
60+
}
61+
62+
// Step 6: Extract URLs from video/audio source elements
63+
preg_match_all(
64+
'#<(?:video|audio|source)[^>]+src=["\']([^"\']+)["\']#i',
65+
$buffer,
66+
$mediaMatches
67+
);
68+
if (!empty($mediaMatches[1])) {
69+
$urls = array_merge($urls, $mediaMatches[1]);
70+
}
1571

16-
$dnsPrefetch = collect($matches[0])->map(function ($item) {
72+
// Filter to keep only external URLs (http:// or https://)
73+
$externalUrls = array_filter($urls, function ($url) {
74+
return preg_match('#^https?://#i', $url);
75+
});
1776

18-
$domain = (new TrimUrls)->apply($item[0]);
77+
$dnsPrefetch = collect($externalUrls)->map(function ($url) {
78+
$domain = (new TrimUrls)->apply($url);
1979
$domain = explode(
2080
'/',
2181
str_replace('//', '', $domain)

src/Middleware/PageSpeed.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,16 @@ public function handle($request, Closure $next)
4949
*/
5050
protected function replace(array $replace, $buffer)
5151
{
52-
return preg_replace(array_keys($replace), array_values($replace), $buffer);
52+
$result = preg_replace(array_keys($replace), array_values($replace), $buffer);
53+
54+
// Check for PCRE errors (e.g., backtrack limit, recursion limit exceeded)
55+
if ($result === null && preg_last_error() !== PREG_NO_ERROR) {
56+
// Log the error or handle it appropriately
57+
// For now, return the original buffer to prevent blank pages
58+
return $buffer;
59+
}
60+
61+
return $result;
5362
}
5463

5564
/**

src/Middleware/RemoveComments.php

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public function apply($buffer)
1111
{
1212
// First, remove multi-line comments (/* ... */)
1313
$buffer = $this->replaceInsideHtmlTags(['script', 'style'], self::REGEX_MATCH_MULTILINE_COMMENTS, '', $buffer);
14-
14+
1515
// Then, remove single-line comments (//) more carefully
1616
$buffer = $this->removeSingleLineComments($buffer);
1717

@@ -21,7 +21,7 @@ public function apply($buffer)
2121

2222
return $this->replace($replaceHtmlRules, $buffer);
2323
}
24-
24+
2525
/**
2626
* Remove single-line comments (//) from script tags while preserving them inside strings
2727
*
@@ -34,10 +34,10 @@ protected function removeSingleLineComments($buffer)
3434
$tagAfterReplace = $this->removeCommentsFromTag($tagMatched);
3535
$buffer = str_replace($tagMatched, $tagAfterReplace, $buffer);
3636
}
37-
37+
3838
return $buffer;
3939
}
40-
40+
4141
/**
4242
* Remove // comments from a script/style tag content
4343
*
@@ -53,18 +53,18 @@ protected function removeCommentsFromTag($tag)
5353
} elseif (strpos($tag, "\r") !== false) {
5454
$lineEnding = "\r";
5555
}
56-
56+
5757
// Split by lines to process each line
5858
$lines = preg_split('/\r\n|\r|\n/', $tag);
5959
$processedLines = [];
60-
60+
6161
foreach ($lines as $line) {
6262
$processedLines[] = $this->removeSingleLineCommentFromLine($line);
6363
}
64-
64+
6565
return implode($lineEnding, $processedLines);
6666
}
67-
67+
6868
/**
6969
* Remove // comment from a single line while preserving // inside strings
7070
*
@@ -79,38 +79,38 @@ protected function removeSingleLineCommentFromLine($line)
7979
$inDoubleQuote = false;
8080
$inRegex = false;
8181
$escaped = false;
82-
82+
8383
for ($i = 0; $i < $length; $i++) {
8484
$char = $line[$i];
8585
$nextChar = $i + 1 < $length ? $line[$i + 1] : '';
8686
$prevChar = $i > 0 ? $line[$i - 1] : '';
87-
87+
8888
// Handle escape sequences
8989
if ($escaped) {
9090
$result .= $char;
9191
$escaped = false;
9292
continue;
9393
}
94-
94+
9595
if ($char === '\\' && ($inSingleQuote || $inDoubleQuote || $inRegex)) {
9696
$result .= $char;
9797
$escaped = true;
9898
continue;
9999
}
100-
100+
101101
// Toggle quote states
102102
if ($char === '"' && !$inSingleQuote && !$inRegex) {
103103
$inDoubleQuote = !$inDoubleQuote;
104104
$result .= $char;
105105
continue;
106106
}
107-
107+
108108
if ($char === "'" && !$inDoubleQuote && !$inRegex) {
109109
$inSingleQuote = !$inSingleQuote;
110110
$result .= $char;
111111
continue;
112112
}
113-
113+
114114
// Handle regex literals (basic detection)
115115
if ($char === '/' && !$inSingleQuote && !$inDoubleQuote) {
116116
// Check if this might be a regex literal
@@ -123,15 +123,15 @@ protected function removeSingleLineCommentFromLine($line)
123123
continue;
124124
}
125125
}
126-
126+
127127
// End of regex literal
128128
if ($inRegex) {
129129
$inRegex = false;
130130
$result .= $char;
131131
continue;
132132
}
133133
}
134-
134+
135135
// Check for // comment outside of strings
136136
if (!$inSingleQuote && !$inDoubleQuote && !$inRegex && $char === '/' && $nextChar === '/') {
137137
// Check if this is not part of a URL (preceded by :)
@@ -140,10 +140,10 @@ protected function removeSingleLineCommentFromLine($line)
140140
break;
141141
}
142142
}
143-
143+
144144
$result .= $char;
145145
}
146-
146+
147147
return $result;
148148
}
149149
}

tests/Middleware/CollapseWhitespaceTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,21 @@ public function test_collapse_whitespace(): void
4343
$this->response->getContent()
4444
);
4545
}
46-
46+
4747
public function test_javascript_not_broken_by_comment_removal_and_whitespace_collapse(): void
4848
{
4949
// This test ensures that when comments are removed and whitespace is collapsed,
5050
// the JavaScript code remains functional and nothing is accidentally commented out
5151
$content = $this->response->getContent();
52-
52+
5353
// Ensure all expected JavaScript statements are present
5454
$this->assertStringContainsString("console.log('Laravel');", $content);
5555
$this->assertStringContainsString("console.log('Page');", $content);
5656
$this->assertStringContainsString("console.log('Speed!');", $content);
5757
$this->assertStringContainsString('var url = "http://example.com";', $content);
5858
$this->assertStringContainsString('var text = "Some text";', $content);
5959
$this->assertStringContainsString("console.log('Important code');", $content);
60-
60+
6161
// Ensure comments are removed
6262
$this->assertStringNotContainsString("// This comment should be removed", $content);
6363
$this->assertStringNotContainsString("// This comment should also be removed", $content);

0 commit comments

Comments
 (0)