jhuddle / framework
极简PHP框架
Requires
- php: >=8.1
README
需要PHP 8.1.0或更高版本
极简PHP,呃,框架。
哦不,又是一个...
这是一个合理的观点 - 但这个框架确实非常小,非常实用,保证。
有多小?
不到10 KB。你几乎感觉不到它的存在。
那么,我该如何开始呢?
让我们从最基本的地方开始;这是一个非常好的开始地方。从你的项目根目录
-
使用 Composer 安装它
composer require jhuddle/framework
-
然后,举例来说,创建以下文件
templates/layout.php
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title><?= $title ?></title> </head> <body> <header> <h1><?= $title ?></h1> </header> <main> <?= $content ?> </main> </body> </html>
templates/partials/list.php
<ul> <?php foreach($items as $item): ?> <li><?= $item ?></li> <?php endforeach; ?> </ul>
public/index.php
<?php // Adjust paths to match your project structure: require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../app/app.php';
app/app.php
<?php // Instantiate app and define it as a constant: define('app', new \Framework\App([ 'view' => dir('../templates'), ])); // Declare routes: app->route->get('/', function () { print app->view('layout', [ 'title' => "Home page", 'content' => "This is the home page.", ]); } ); app->route->get('/hello/<name?>', fn ($name = 'world') => print app->view('layout', [ 'title' => "Hello, $name", 'content' => null, ]) ); app->route->get('/goodbye/<goodbye?string>/<times:int>', fn ($times, $goodbye = 'goodbye') => print app->view('layout', [ 'title' => "Goodbye...", 'content' => str_repeat( "<p>So long, farewell, auf Wiedersehen, $goodbye!</p>", $times ), ]) ); app->route->get('/favorite-things', fn () => print app->view('layout', [ 'title' => "A few of my favorite things", 'content' => app->view('partials/list', [ 'items' => [ "raindrops on roses", "whiskers on kittens", "bright copper kettles", "warm woolen mittens", "brown paper packages tied up with strings", ], ]), ]) ); app->route->post('/apply-now', function () { $data = json_decode(app->input); $name = htmlspecialchars(@$data->name); $role = htmlspecialchars(@$data->role); if ($name && $role) { print app->view('layout', [ 'title' => "Thank you, $name", 'content' => "Your application for the role of $role is being considered." ]); } else { http_response_code(400); // Bad Request print app->view('layout', [ 'title' => "Sorry", 'content' => "There was a problem with your application; please try again." ]); } } ); // Fallback: http_response_code(404); // Not Found print '<h1>404 Not Found :(</h1>';
-
启动一个指向
public/index.php
的Web服务器 -
然后,从你的基本URL(例如
https://:8000
),在浏览器中选择尝试一些路由/ /hello /hello/captain /goodbye/5 /goodbye/adieu/3 /goodbye/goodnight /favorite-things
或使用 curl
curl localhost:8000/apply-now -d '{"name": "Maria", "role": "Governess"}'
请注意,在每种情况下,路由处理方法 get()
和 post()
- 或 put()
、patch()
、delete()
,无论你需要什么 - 都接受两个参数
- 路由模式,可能包含尖括号内的参数:这些参数可以用
?
使其可选,并/或使用PHP标量类型名称作为语法提示(尽管出于安全原因,不会自动执行类型强制/URL解码) - 回调函数,具有与路由模式中对应的命名参数,其中可以使用应用程序的
input
属性来检查请求正文
希望你现在可能已经知道它是如何工作的了...
是的,看起来很简单 - 那么 'view' => dir('../templates')
是怎么回事呢?
这告诉你的 App
实例在哪里可以找到生成网站HTML的PHP模板;框架不期望任何特定的文件夹结构,所以你需要告诉它从哪个基本文件夹开始查找。实际上,数组键甚至不必命名为 view
:你可以称它为 template
、html
或你喜欢的任何其他名称!你甚至可以有多个数组键,每个键都指向不同的基本文件夹。
无论你选择什么名称,都会用来创建创建 类 的方法,这些类会包含传递给它的路径建议的文件,并提取可选的变量名称和值数组以供该文件使用,例如
<?php define('app', new \Framework\App([ 'layout' => dir('../templates/layouts'), 'partial' => dir('../templates/partials'), ])); print app->layout('song/lyrics', [ 'title' => "The Lonely Goatherd", 'content' => implode("<br>", [ "High on a hill was a lonely goatherd,", app->partial('yodel'), "Loud was the voice of the lonely goatherd,", app->partial('yodel'), "Folks in a town that was quite remote heard", app->partial('yodel'), "Lusty and clear from the goatherd's throat, heard", app->partial('yodel'), ]), ]);
但这还不止这些!除了包裹在 dir()
中的基本文件夹,你可以在任何时间向应用程序实例添加/覆盖任何内容
<?php define('app', new \Framework\App([ 'notes' => ["Do", "Re", "Mi",], 'lineFrom' => fn ($a, $b) => "$a - $b,\n", ])); print "When you read you begin with A-B-C.\n"; print "When you sing you begin with " . implode("-", app->notes) . ".\n"; app->use([ 'notes' => ["Do", "Re", "Mi", "Fa", "So", "La", "Ti",], 'memory_aids' => [ "a deer, a female deer", "a drop of golden sun", "a name I call myself", "a long, long way to run", "a needle pulling thread", "a note to follow So", "a drink with jam and bread", ], ]); foreach (array_combine(app->notes, app->memory_aids) as $note => $memory_aid) { print app->lineFrom($note, $memory_aid); } print "That will bring us back to " . app->notes[0] . "!";
可爱。它还能做什么?
它不仅处理请求,还可以发起请求
<?php define('app', new \Framework\App()); app->route->get('/html', fn () => print app->request->get( 'https://github.com/jhuddle/framework/blob/main/README.md' ) ); app->route->get('/json/<data:string>', function ($data) { $round_trip = app->request->post( 'https://httpbin.org/anything', [ 'Content-Type' => 'text/plain', 'X-Powered-By' => 'jhuddle/framework', ], $data ); print "<h1>" . $round_trip->headers['Content-Type'] . "</h1>"; print "<pre>" . $round_trip . "</pre>"; } );
幕后,route
和 request
对象以与上面相同的方式加载到应用程序中——因此,如果您想使用不同的实现,可以在构造函数或使用 use()
方法中覆盖它们。
同样适用于 db
—— 如果执行上下文中存在正确的环境变量,框架将基于 DBMS
的值建立 PDO 连接,使用 DB_HOST
、DB_PORT
、DB_NAME
、DB_USER
和 DB_PASS
作为凭证。内置实现包含了一些常见的辅助方法,用于处理预编译语句和事务,但您可以根据需要用另一个实现来覆盖 db
。
<?php // Using default implementation with environment variables // `DBMS=mariadb` + `DB_` credentials (see above): define('app', new \Framework\App()); $something_good = app->db->execute( ' SELECT thing FROM youth INNER JOIN childhood ON youth.person_id = childhood.person_id WHERE thing LIKE ? LIMIT 1 ', ['%good%'] ); // Switching to SQLite: app->use([ 'db' => new \PDO('sqlite:nothing.sqlite') ])); $query = app->db->query(' SELECT thing FROM youth INNER JOIN childhood ON youth.person_id = childhood.person_id WHERE thing IS NULL '); $nothing = $query->fetchAll(\PDO::FETCH_OBJ);
还有 session
,这是一个非常简单的内置 PHP 会话管理函数的包装器——默认情况下,除非将 false
作为第二个参数传递给 App
构造函数,否则框架将启动会话。
好的,听起来不错——这就是全部了吗?
是的,但也不完全是这样!我们已经接触到了构成此框架的六个小类,但您还可以用它们做更多的事情...
-
您使用
dir()
创建的类可以存储在变量中,并使用use()
方法扩展,就像应用程序本身一样。templates/mantra.php
<p>I have <?= $attribute ?> in <?= $item ?>!</p>
app/app.php
<?php define('app', new \Framework\App([ 'view' => dir('../templates'), ])); $confidence = app->view('mantra', [ 'attribute' => 'confidence', 'item' => 'me', ]); app->route->get('/confidence/<item>', fn ($item) => print $confidence->use(['item' => $item]) );
-
这些类也可以被调用!您可以使用这一事实来简化路由回调。
templates/hills.php
<p>The hills are alive with the sound of <?= $noise ?>...</p>
app/app.php
<?php define('app', new \Framework\App([ 'view' => dir('../templates'), ])); app->route->get('/sound/<noise?>', app->view('hills', ['noise' => 'music']) );
-
模板也可以创建和调用这些类。
templates/layout.php
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title><?= $title ?></title> </head> <body> <header> <?= app->view('partials/header', ['title' => $title]) ?> </header> <main> <?= $content ?> </main> <footer> <?= app->view('partials/footer') ?> </footer> </body> </html>
templates/partials/header.php
<h1><?= $title ?></h1>
templates/partials/footer.php
<aside>Fun fact: not the national anthem of Austria.</aside>
app/app.php
<?php define('app', new \Framework\App([ 'view' => dir('../templates'), ])); app->route->get('/edelweiss', app->view('layout', [ 'title' => "Edelweiss", 'content' => "<p>Every morning you greet me!<p>", ]) );
-
可以将路由方法链接在一起,并且/或者可以添加前缀。
<?php class RelationshipController { public static function depend() { print "I'll depend on you"; } public static function takeCare() { print "I'll take care of you"; } public static function prepare(string $men) { $sanitized_men = htmlspecialchars(urldecode($men)); print "$sanitized_men - what do I know of those?"; } } define('app', new \Framework\App()); app->route->prefix('sixteen') ->get('/seventeen', [RelationshipController::class, 'depend']) ->get('/eighteen', [RelationshipController::class, 'takeCare']) ->get('/<men:string>', [RelationshipController::class, 'prepare']) ;
可能还有许多其他尚未想到的用例的可用模式...这个项目的目标是编写最小的、最少偏见、最可扩展和最有用的 PHP 框架,所以从现在起,就靠您和您的想象力了!
太棒了,谢谢!我如何贡献?
您对这个框架的最佳贡献就是简单地使用它,并报告任何问题...如果您用它构建了酷炫的东西,请通过在 Projects 文件中添加指向您的 GitHub 仓库的链接来让我知道。我会很高兴看到您的成果。🙂