Skip to content

Commit 647845f

Browse files
markcclaude
andcommitted
Refactor 05-Autoload to app shell pattern
- Migrate from theme-switching to single app shell layout - Remove Themes/ folder (Simple, TopNav, SideBar) - Condense Core classes (Ctx, Init, Plugin, View, Theme) - Remove unused AboutView (falls back to base View) - Add comprehensive documentation to HomeView - Fix button classes (btn btn-success/danger) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 44c9807 commit 647845f

File tree

14 files changed

+141
-288
lines changed

14 files changed

+141
-288
lines changed

05-Autoload/src/Core/Ctx.php

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Core;
65

76
final class Ctx
87
{
98
public array $in;
10-
public array $out;
119

1210
public function __construct(
1311
public string $email = 'mc@netserva.org',
14-
array $in = ['o' => 'Home', 'm' => 'list', 't' => 'Simple', 'x' => ''],
15-
array $out = ['doc' => 'SPE::05', 'head' => '', 'main' => '', 'foot' => ''],
16-
public array $nav = [
17-
['home', 'Home', 'Home'],
18-
['book-open', 'About', 'About'],
19-
['mail', 'Contact', 'Contact'],
20-
],
21-
public array $themes = [
22-
['layout-template', 'Simple', 'Simple'],
23-
['navigation', 'TopNav', 'TopNav'],
24-
['panel-left', 'SideBar', 'SideBar'],
25-
],
12+
array $in = ['o' => 'Home', 'm' => 'list', 'x' => ''],
13+
public array $out = ['doc' => 'SPE::05', 'page' => '← 05 Autoload', 'head' => '', 'main' => '', 'foot' => ''],
14+
public array $nav = [['home', 'Home', 'Home'], ['book-open', 'About', 'About'], ['mail', 'Contact', 'Contact']],
15+
public array $colors = [['circle', 'Stone', 'default'], ['waves', 'Ocean', 'ocean'], ['trees', 'Forest', 'forest'], ['sunset', 'Sunset', 'sunset']],
2616
) {
27-
$this->in = array_map(
28-
static fn($k, $v) => ($_REQUEST[$k] ?? $v) |> trim(...) |> htmlspecialchars(...),
29-
array_keys($in),
30-
$in,
31-
)
32-
|> (static fn($v) => array_combine(array_keys($in), $v));
33-
$this->out = $out;
17+
$this->in = array_map(fn($k, $v) => ($_REQUEST[$k] ?? $v) |> trim(...) |> htmlspecialchars(...), array_keys($in), $in)
18+
|> (fn($v) => array_combine(array_keys($in), $v));
3419
}
3520
}

05-Autoload/src/Core/Init.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Core;
65

76
final readonly class Init
87
{
98
private const string NS = 'SPE\\Autoload\\';
10-
119
private array $out;
1210

13-
public function __construct(
14-
private Ctx $ctx,
15-
) {
16-
[$o, $m, $t] = [$ctx->in['o'], $ctx->in['m'], $ctx->in['t']];
17-
11+
public function __construct(private Ctx $ctx)
12+
{
13+
[$o, $m] = [$ctx->in['o'], $ctx->in['m']];
1814
$model = self::NS . "Plugins\\{$o}\\{$o}Model";
1915
$ary = class_exists($model) ? new $model($ctx)->$m() : [];
20-
2116
$view = self::NS . "Plugins\\{$o}\\{$o}View";
22-
$main = class_exists($view) ? new $view($ctx, $ary)->$m() : "<p>{$ary['main']}</p>";
23-
17+
$main = class_exists($view) ? new $view($ctx, $ary)->$m() : new View($ctx, $ary)->$m();
2418
$this->out = [...$ctx->out, ...$ary, 'main' => $main];
2519
}
2620

2721
public function __toString(): string
2822
{
29-
$t = $this->ctx->in['t'];
30-
$theme = self::NS . "Themes\\{$t}";
3123
return match ($this->ctx->in['x']) {
3224
'json' => (header('Content-Type: application/json') ?: '') . json_encode($this->out),
33-
default => new $theme($this->ctx, $this->out)->render(),
25+
default => new Theme($this->ctx, $this->out)->render(),
3426
};
3527
}
3628
}

05-Autoload/src/Core/Plugin.php

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,10 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Core;
65

76
abstract class Plugin
87
{
9-
public function __construct(
10-
protected Ctx $ctx,
11-
) {}
12-
13-
public function create(): array
14-
{
15-
return ['head' => 'Create', 'main' => 'Not implemented'];
16-
}
17-
18-
public function read(): array
19-
{
20-
return ['head' => 'Read', 'main' => 'Not implemented'];
21-
}
22-
23-
public function update(): array
24-
{
25-
return ['head' => 'Update', 'main' => 'Not implemented'];
26-
}
27-
28-
public function delete(): array
29-
{
30-
return ['head' => 'Delete', 'main' => 'Not implemented'];
31-
}
32-
33-
public function list(): array
34-
{
35-
return ['head' => 'List', 'main' => 'Not implemented'];
36-
}
8+
public function __construct(protected Ctx $ctx) {}
9+
public function list(): array { return ['head' => 'List', 'main' => 'Not implemented']; }
3710
}

05-Autoload/src/Core/Theme.php

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,73 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Core;
65

7-
abstract class Theme
6+
final class Theme
87
{
9-
public function __construct(
10-
protected Ctx $ctx,
11-
protected array $out,
12-
) {}
8+
public function __construct(private Ctx $ctx, private array $out) {}
9+
10+
public function render(): string
11+
{
12+
return $this->html($this->topnav() . $this->sidebar('left') . $this->sidebar('right') . "<main>{$this->out['main']}</main>");
13+
}
1314

14-
abstract public function render(): string;
15+
private function navLinks(): string
16+
{
17+
return implode('', array_map(fn($p) => sprintf(
18+
'<a href="?o=%s"%s data-icon="%s"><i data-lucide="%s"></i> %s</a>',
19+
$p[2], $this->ctx->in['o'] === $p[2] ? ' class="active"' : '', $p[0], $p[0], $p[1]
20+
), $this->ctx->nav));
21+
}
1522

16-
protected function nav(): string
23+
private function colorLinks(): string
1724
{
18-
['o' => $o, 't' => $t] = $this->ctx->in;
19-
return $this->ctx->nav
20-
|> (static fn($n) => array_map(static fn($p) => sprintf(
21-
'<a href="?o=%s&t=%s"%s><i data-lucide="%s"></i> %s</a>',
22-
$p[2],
23-
$t,
24-
$o === $p[2] ? ' class="active"' : '',
25-
$p[0],
26-
$p[1],
27-
), $n))
28-
|> (static fn($a) => implode(' ', $a));
25+
return implode('', array_map(fn($p) => sprintf(
26+
'<a href="#" data-scheme="%s" data-icon="%s"><i data-lucide="%s"></i> %s</a>', $p[2], $p[0], $p[0], $p[1]
27+
), $this->ctx->colors));
2928
}
3029

31-
protected function dropdown(): string
30+
private function topnav(): string
3231
{
33-
['o' => $o, 't' => $t] = $this->ctx->in;
34-
$links = $this->ctx->themes
35-
|> (static fn($n) => array_map(static fn($p) => sprintf(
36-
'<a href="?o=%s&t=%s"%s><i data-lucide="%s"></i> %s</a>',
37-
$o,
38-
$p[2],
39-
$t === $p[2] ? ' class="active"' : '',
40-
$p[0],
41-
$p[1],
42-
), $n))
43-
|> (static fn($a) => implode('', $a));
44-
return "<div class=\"dropdown\"><span class=\"dropdown-toggle\"><i data-lucide=\"layout-grid\"></i> Layout</span><div class=\"dropdown-menu\">$links</div></div>";
32+
return <<<HTML
33+
<nav class="topnav">
34+
<button class="menu-toggle" data-sidebar="left"><i data-lucide="menu"></i></button>
35+
<h1><a class="brand" href="/"><span>{$this->out['page']}</span></a></h1>
36+
<button class="menu-toggle" data-sidebar="right"><i data-lucide="menu"></i></button>
37+
</nav>
38+
HTML;
4539
}
4640

47-
protected function colors(): string
41+
private function sidebar(string $side): string
4842
{
43+
[$nav, $title, $icon] = $side === 'left'
44+
? [$this->navLinks(), 'Navigation', 'compass']
45+
: [$this->colorLinks() . '<div class="sidebar-divider"></div><a href="#" onclick="Base.toggleTheme();return false" data-icon="moon"><i data-lucide="moon"></i> Toggle Theme</a>', 'Settings', 'sliders-horizontal'];
4946
return <<<HTML
50-
<div class="dropdown"><span class="dropdown-toggle"><i data-lucide="swatch-book"></i> Colors</span><div class="dropdown-menu">
51-
<a href="#" data-scheme="default"><i data-lucide="circle"></i> Stone</a>
52-
<a href="#" data-scheme="ocean"><i data-lucide="waves"></i> Ocean</a>
53-
<a href="#" data-scheme="forest"><i data-lucide="trees"></i> Forest</a>
54-
<a href="#" data-scheme="sunset"><i data-lucide="sunset"></i> Sunset</a>
55-
</div></div>
47+
<aside class="sidebar sidebar-{$side}">
48+
<div class="sidebar-header"><span><i data-lucide="{$icon}"></i> {$title}</span><button class="pin-toggle" data-sidebar="{$side}" title="Pin sidebar"><i data-lucide="pin"></i></button></div>
49+
<nav>{$nav}</nav>
50+
</aside>
5651
HTML;
5752
}
5853

59-
protected function html(string $theme, string $body): string
54+
private function html(string $body): string
6055
{
6156
return <<<HTML
6257
<!DOCTYPE html>
6358
<html lang="en">
6459
<head>
6560
<meta charset="utf-8">
6661
<meta name="viewport" content="width=device-width, initial-scale=1">
67-
<title>{$this->out['doc']} [$theme]</title>
62+
<title>{$this->out['doc']}</title>
6863
<link rel="stylesheet" href="/base.css">
6964
<link rel="stylesheet" href="/site.css">
7065
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
71-
<script>(function(){const t=localStorage.getItem("base-theme"),s=localStorage.getItem("base-scheme"),c=t||(matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light");document.documentElement.className=c+(s&&s!=="default"?" scheme-"+s:"")})();</script>
66+
<script>(function(){var s=JSON.parse(localStorage.getItem('base-state')||'{}'),t=s.theme,c=s.scheme,h=document.documentElement;h.className='preload '+(t||(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'))+(c&&c!=='default'?' scheme-'+c:'');})()</script>
7267
</head>
7368
<body>
74-
$body
69+
{$body}
70+
<div class="overlay"></div>
7571
<script src="/base.js"></script>
7672
</body>
7773
</html>

05-Autoload/src/Core/View.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Core;
65

76
class View
87
{
9-
public function __construct(
10-
protected Ctx $ctx,
11-
protected array $ary,
12-
) {}
13-
14-
public function list(): string
15-
{
16-
return <<<HTML
17-
<div class="card">
18-
<h2>{$this->ary['head']}</h2>
19-
<p>{$this->ary['main']}</p>
20-
</div>
21-
HTML;
22-
}
8+
public function __construct(protected Ctx $ctx, protected array $ary) {}
9+
public function list(): string { return "<div class=\"card\"><h2>{$this->ary['head']}</h2><p>{$this->ary['main']}</p></div>"; }
2310
}
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Plugins\About;
@@ -11,9 +10,6 @@ final class AboutModel extends Plugin
1110
#[\Override]
1211
public function list(): array
1312
{
14-
return [
15-
'head' => 'About Page',
16-
'main' => 'This chapter adds <b>PSR-4 autoloading</b> via Composer to organize classes into separate files.',
17-
];
13+
return ['head' => 'About Page', 'main' => 'This chapter adds <b>PSR-4 autoloading</b> via Composer to organize classes into separate files.'];
1814
}
1915
}

05-Autoload/src/Plugins/About/AboutView.php

Lines changed: 0 additions & 9 deletions
This file was deleted.

05-Autoload/src/Plugins/Contact/ContactModel.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Plugins\Contact;

05-Autoload/src/Plugins/Contact/ContactView.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Plugins\Contact;
Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php declare(strict_types=1);
2-
32
// Copyright (C) 2015-2026 Mark Constable <mc@netserva.org> (MIT License)
43

54
namespace SPE\Autoload\Plugins\Home;
@@ -12,23 +11,8 @@ final class HomeModel extends Plugin
1211
public function list(): array
1312
{
1413
return [
15-
'head' => 'Home Page',
16-
'main' => '<p>Welcome to the <b>Autoload</b> chapter demonstrating PSR-4 autoloading via Composer.</p>
17-
<h3>What\'s New</h3>
18-
<ul>
19-
<li><b>PSR-4 Autoloading</b> — Classes organized into <code>src/</code> with namespace <code>SPE\Autoload</code></li>
20-
<li><b>Lucide Icons</b> — SVG icon library replaces emoji icons for a polished look</li>
21-
<li><b>Directory Structure</b> — Separated into <code>Core/</code>, <code>Plugins/</code>, and <code>Themes/</code></li>
22-
<li><b>Composer Integration</b> — Dependency management and class autoloading</li>
23-
</ul>
24-
<h3>Directory Layout</h3>
25-
<pre>05-Autoload/
26-
├── composer.json
27-
├── public/index.php
28-
└── src/
29-
├── Core/ # Ctx, Init, Plugin, Theme, View
30-
├── Plugins/ # Home/, About/, Contact/
31-
└── Themes/ # Simple, TopNav, SideBar</pre>',
14+
'head' => 'PSR-4 Autoloading',
15+
'main' => '<p>This chapter introduces <b>Composer</b> and <b>PSR-4 autoloading</b> to organize classes into separate files with proper namespaces.</p>',
3216
];
3317
}
3418
}

0 commit comments

Comments
 (0)