Skip to content

Commit 2c0d0e5

Browse files
committed
feat: complete PHP tutorial site with favicon, dark mode, and module navigation
- Add favicon.ico reference in HTML head - Implement dark/light mode toggle with localStorage persistence - Switch to module-based content loading with one module visible at a time - Add large previous/next module pagination buttons - Add floating scroll-to-top button - Fix sidebar to be fixed below topnav and left of content - Fix composer.json PSR-4 namespace prefix and unclosed span - Harden password_hash example, fix email validation, qualify JIT claim - Improve accessibility with skip link, aria-current, and contrast
1 parent 535209c commit 2c0d0e5

2 files changed

Lines changed: 26 additions & 10 deletions

File tree

favicon.ico

4.19 KB
Binary file not shown.

index.html

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>Modern PHP 8.x Masterclass — Complete Guide</title>
77
<meta name="description" content="A comprehensive, modern guide to PHP 8.x programming covering beginner to advanced topics.">
8+
<link rel="icon" href="favicon.ico" type="image/x-icon">
9+
<script>
10+
(function () {
11+
const stored = localStorage.getItem('php-guide-dark-mode');
12+
const dark = stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
13+
if (dark) document.documentElement.classList.add('dark');
14+
})();
15+
</script>
816
<script src="https://cdn.tailwindcss.com"></script>
917
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js"></script>
1018
<script>
@@ -419,7 +427,7 @@ <h4 class="text-xl font-bold text-github-900 dark:text-github-100 flex items-cen
419427
<div class="mt-4 space-y-4 text-github-700 dark:text-github-300 leading-relaxed">
420428
<h5 class="font-semibold text-github-900 dark:text-github-100">Conceptual Explanation</h5>
421429
<p>PHP provides <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">for</code>, <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">while</code>, <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">do-while</code>, and <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">foreach</code>. The <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">foreach</code> loop is the canonical tool for iterating arrays and objects implementing <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">Traversable</code>. By default it iterates over the array without modifying it; use <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">foreach ($items as &amp;$item)</code> to mutate the original array in place.</p>
422-
<p>Modern PHP favors <strong>functional-style transformations</strong> (<code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">array_map</code>, <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">array_filter</code>) or generators for large datasets instead of manual loops. The Zend Engine optimizes <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">foreach</code> over arrays efficiently by tracking an internal array pointer, but generators avoid materializing entire collections in memory.</p>
430+
<p>Modern PHP favors <strong>functional-style transformations</strong> (<code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">array_map</code>, <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">array_filter</code>) or generators for large datasets instead of manual loops. The Zend Engine optimizes <code class="bg-github-100 dark:bg-github-800 dark:text-github-200 px-1 py-0.5 rounded text-sm font-mono">foreach</code> with copy-on-write iterators, but generators still avoid materializing entire collections in memory.</p>
423431

424432
<h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Code Example</h5>
425433
<div class="rounded-lg bg-github-900 overflow-hidden text-sm my-4">
@@ -606,7 +614,7 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Co
606614
<span class="line">$rawEmail = $_POST['email'] ?? '';</span>
607615
<span class="line">$email = filter_var($rawEmail, FILTER_VALIDATE_EMAIL);</span>
608616
<span class="line"></span>
609-
<span class="line">if ($email === false || $email === null) {</span>
617+
<span class="line">if ($email === false) {</span>
610618
<span class="line"> http_response_code(422);</span>
611619
<span class="line"> echo 'Invalid email address.';</span>
612620
<span class="line"> exit;</span>
@@ -650,6 +658,7 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Co
650658
<span class="line">session_start();</span>
651659
<span class="line"></span>
652660
<span class="line">// After verifying credentials, rotate the session ID to prevent fixation</span>
661+
<span class="line">// authenticateUser() is your credential-checking routine (returns int|null)</span>
653662
<span class="line">$userId = authenticateUser($_POST['email'] ?? '', $_POST['password'] ?? '');</span>
654663
<span class="line"></span>
655664
<span class="line">if ($userId !== null) {</span>
@@ -1027,9 +1036,9 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Co
10271036
<span class="line"> "phpunit/phpunit": "^10.5",</span>
10281037
<span class="line"> "phpstan/phpstan": "^1.10"</span>
10291038
<span class="line"> },</span>
1030-
<span class="line"> "autoload": {
1039+
<span class="line"> "autoload": {</span>
10311040
<span class="line"> "psr-4": {</span>
1032-
<span class="line"> "App\\\\": "src/"</span>
1041+
<span class="line"> "App\\": "src/"</span>
10331042
<span class="line"> }</span>
10341043
<span class="line"> },</span>
10351044
<span class="line"> "autoload-dev": {</span>
@@ -1438,7 +1447,7 @@ <h4 class="text-xl font-bold text-github-900 dark:text-github-100 flex items-cen
14381447
<div class="mt-4 space-y-4 text-github-700 dark:text-github-300 leading-relaxed">
14391448
<h5 class="font-semibold text-github-900 dark:text-github-100">Conceptual Explanation</h5>
14401449
<p>The <strong>Zend Engine</strong> compiles PHP scripts into bytecode (opcodes), executes them on a virtual machine, and manages memory through reference counting with a cycle-collecting garbage collector. Objects and arrays are reference-counted; when the count drops to zero, memory is reclaimed. Circular references are detected by the garbage collector.</p>
1441-
<p><strong>OPcache</strong> stores compiled bytecode in shared memory so that PHP does not recompile files on every request. It is the single most important performance extension for production PHP. PHP 8.0+ also includes a <strong>JIT (Just-In-Time)</strong> compiler that translates hot bytecode into native machine code, significantly improving CPU-bound workloads.</p>
1450+
<p><strong>OPcache</strong> stores compiled bytecode in shared memory so that PHP does not recompile files on every request. It is the single most important performance extension for production PHP. PHP 8.0+ also includes a <strong>JIT (Just-In-Time)</strong> compiler that translates hot bytecode into native machine code; it can improve CPU-bound workloads, but typical I/O-bound web requests often see modest gains.</p>
14421451

14431452
<h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Code Example</h5>
14441453
<div class="rounded-lg bg-github-900 overflow-hidden text-sm my-4">
@@ -1481,11 +1490,18 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Co
14811490
<span class="line"></span>
14821491
<span class="line">// Hash a password</span>
14831492
<span class="line">$hash = password_hash($plainPassword, PASSWORD_DEFAULT);</span>
1493+
<span class="line">if ($hash === false) {</span>
1494+
<span class="line"> throw new RuntimeException('Password hashing failed.');</span>
1495+
<span class="line">}</span>
14841496
<span class="line"></span>
14851497
<span class="line">// Verify on login</span>
14861498
<span class="line">if (password_verify($plainPassword, $hash)) {</span>
14871499
<span class="line"> if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {</span>
1488-
<span class="line"> $hash = password_hash($plainPassword, PASSWORD_DEFAULT);</span>
1500+
<span class="line"> $newHash = password_hash($plainPassword, PASSWORD_DEFAULT);</span>
1501+
<span class="line"> if ($newHash === false) {</span>
1502+
<span class="line"> throw new RuntimeException('Password rehashing failed.');</span>
1503+
<span class="line"> }</span>
1504+
<span class="line"> $hash = $newHash;</span>
14891505
<span class="line"> // store updated hash</span>
14901506
<span class="line"> }</span>
14911507
<span class="line">}</span>
@@ -1494,8 +1510,8 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Idiomatic Co
14941510
<span class="line">$safe = htmlspecialchars($userInput, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');</span>
14951511
<span class="line">echo "&lt;p&gt;{$safe}&lt;/p&gt;";</span>
14961512
<span class="line"></span>
1497-
<span class="line">// Secure headers</span>
1498-
<span class="line">header('Content-Security-Policy: default-src \'self\'');</span>
1513+
<span class="line">// Secure headers. In production, serve inline scripts/styles with nonces/hashes instead of 'unsafe-inline'.</span>
1514+
<span class="line">header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'");</span>
14991515
<span class="line">header('X-Frame-Options: DENY');</span>
15001516
<span class="line">header('Strict-Transport-Security: max-age=31536000; includeSubDomains');</span></code></pre>
15011517
</div>
@@ -1601,7 +1617,7 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Common Anti-
16011617
@click="goToPreviousModule()"
16021618
class="flex-1 flex items-center justify-start gap-3 rounded-xl border border-github-200 bg-white px-5 py-4 text-left shadow-sm transition hover:border-php-300 hover:bg-php-50 dark:border-github-700 dark:bg-github-900 dark:hover:border-php-700 dark:hover:bg-php-900/20"
16031619
>
1604-
<svg class="h-5 w-5 flex-shrink-0 text-github-400 dark:text-github-500 dark:text-github-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
1620+
<svg class="h-5 w-5 flex-shrink-0 text-github-400 dark:text-github-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/></svg>
16051621
<div>
16061622
<span class="block text-xs font-medium uppercase tracking-wider text-github-500 dark:text-github-400">Previous module</span>
16071623
<span class="block text-base font-semibold text-github-900 dark:text-github-100" x-text="previousModule ? previousModule.title : ''"></span>
@@ -1616,7 +1632,7 @@ <h5 class="font-semibold text-github-900 dark:text-github-100 pt-4">Common Anti-
16161632
<span class="block text-xs font-medium uppercase tracking-wider text-github-500 dark:text-github-400">Next module</span>
16171633
<span class="block text-base font-semibold text-github-900 dark:text-github-100" x-text="nextModule ? nextModule.title : ''"></span>
16181634
</div>
1619-
<svg class="h-5 w-5 flex-shrink-0 text-github-400 dark:text-github-500 dark:text-github-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
1635+
<svg class="h-5 w-5 flex-shrink-0 text-github-400 dark:text-github-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
16201636
</button>
16211637
</div>
16221638
</nav>

0 commit comments

Comments
 (0)