rossato / declarative-php
PHP构建声明式和静态Web应用的微框架
Requires
- php: >=5.3.0
Requires (Dev)
- phpunit/phpunit: ^4.8
- squizlabs/php_codesniffer: ^3.4
README
🔨 PHP中构建声明式Web应用的简单微框架 📰
受ReactDOM启发的最小实验性PHP框架,用于以声明方式创建网页。
通过PHP类创建动态元素,并将其渲染为HTML。
入门
安装此框架后,使用composer require rossato/declarative-php
,您可以使用Element构造函数构建元素:类构造函数接收3个参数:元素标签名称、属性数组以及元素内容(其子元素)
require "vendor/autoload.php" use Rossato\Element; $div = new Element( "div", // tag name ["class" => "custom"], // properties (as associative array) "text" // content ); echo $div;
这将输出一个具有custom
类和内容text
的HTML div
元素,如下所示:<div class="custom">text</div>
嵌套示例
元素可以嵌套,第三个、第四个等参数可以是其他元素
use Rossato\Element; echo new Element( "ul", ["style" => "margin: 10px"], new Element("li", null, "First element"), new Element("li", null, "Second element") );
您还可以提供元素数组作为子元素
use Rossato\Element; echo new Element( "form", ["style" => "margin: 10px"], [ new Element("input", ["type" => "text", "id" => "hello"]), ] );
生成以下内容
<form style="width:10px"><input type="text" id="hello"/></form>
功能用法
您应该创建这样的组件
use Rossato\Element; function Form($url) { return new Element("form", ["action" => $url], new Element("input", ["type" => "submit", "value" => "Submit"]) ); } function App() { return new Element("div", [], Form("/send/"), Form("/submit/") ]); } echo App();
页面示例
Page元素对于执行顶层操作非常有用,它应该是页面的HTML标签元素
use Rossato\Element; use Rossato\Page; $page = new Page([ new Element("title", [], "Title in the head tag") ], [ new Element("h1", [], "Title in the body tag") ]); echo $page; // "<html><head><title>Title in the head tag</title></head><body><h1>Title in the body tag</h1></body></html>"
灵感
React(JavaScript库/框架)在定义组件及其层次关系方面有独特的方法。这个实验旨在探索PHP上的这一方面。
PHP已经内部有一个DOM库,但它包含许多功能,如查询和解析,这个库只专注于构建经过清理的HTML元素,这样您就可以只做事情。这个库只有2个源文件大,并且处理属性和内容清理。
$userInput = "what\"><script>console.log('nope')</script><div "; echo new Element("div", ["class" => $userInput], "Content")); // "<div class="what%22%3E%3Cscript%3Econsole.log('nope')%3C%2Fscript%3E%3Cdiv%20">Content</div>"
这个库并不是为了复杂,以下是对Element类中元素内容创建的过度简化
class Element { function __toString() { return "<" . $this->$tag . ">" . escapeHTML($this->$content) . "</" . $this->$tag . ">"; } }
您可能可以在15分钟内阅读和理解这个库。
您可以使用此库解决的问题
由于HTML通常不严格验证,在PHP中将数据转换为HTML很困难
<?php $price = $data["price"]; ?> <div class="price-box"> <span class="label">Price:</span> <span class="value"><?php echo $price; ?></spen> </div>
您认为浏览器会通知您在第二个闭合标签上拼写错误'span'吗?不会的,浏览器只是默默继续。
您可以利用PHP解释器来告诉您是否有语法错误,这些错误会导致快速失败。
想法是在基于组合的组件中声明所有视觉逻辑,以及它渲染所需的全部数据。
React开发者已经在JavaScript中这样做,这非常有用,相同的编码规则也适用。遗憾的是,实现JSX是一项巨大的工作,我认为PHPX不会流行。
管理数据
数据必须以自顶向下的方式传递
function Post($index, $title, $text) { return new Element( "article", ["class"="article-".$index], new Element("h2", [], $title), new Element("p", [], $text), ); } function PostList($postData) { $postList = []; $index = 0; foreach ($postsData as $name => $post) { $index++; $postList[] = new Post($index, $name, $post); } return new Element("div", ["class" => "post-list"], $postList); } $postsData = [ "My life as a developer" => "It was fun", "I'm declaring" => "data like normal", "And html elements treat" => "and using it easily" ]; echo PostList($postData); // Renders pure html (but it may also throw if rendering fails)
显然,关于如何以及在哪里获取数据,您将完全独立。但请注意:所有安全规则仍然适用,您必须从您的数据中移除标签!。
安装
但是您应该使用composer
从命令行在项目的文件夹中安装项目
composer require rossato/declarative-php
但请记住在您的入口点要求composer的自动加载
require "vendor/autoload.php";
在您需要使用Element类的每个文件的顶部,您写下以下内容
use Rossato\Element;
现在每次您写Element
时,都会引用此项目的Element。
或者,您可以下载这个存储库,并手动要求 /src/
目录下的文件,无需提供任何信用。
元素中样式自动压缩
好吧,这个库中只有一项魔法:html元素的style
属性在两个方面都有很强的特性
- 如果您在该属性中放入一个对象,它将成为该对象的字符串表示形式
$style = [ "width" => 200, "height" => 200, "background-color" => "red" ]; echo new Element("div", ["style" => $style]);;
<div style="width:200px,height:200px,background-color:red"/>
- 但如果您放入一个原始字符串,我们将通过一个简化的函数来最小化它,以节省一些字节
```php
echo new Element("div", ["style" => "/* Text color comment */ color : red; background : blue; "]);
<div style="color:red;background:blue"/>
CSS是一种如此简单的语言,因此我们希望压缩它不会有任何副作用。
JavaScript
您可以告诉这个库忽略关于html转义的问题
$script = new Element("script", [], "console.log('hi <div></div>');");
echo $script; // '<script>console.log('hi >div<>div<');</script>"
$script->isRawHTML = true;
echo $script; // '<script>console.log('hi <div></div>');</script>"
显然没有进行任何JavaScript压缩,因为这需要相当多的代码。
使用建议
我认为您不应该用这个工具编写整个Web应用(但如果您这样做,请让我知道),只有在数据会严重影响输出结构的情况下才使用。
例如,当您获取应用列表时,可以使用这个工具轻松组合列表并将其作为HTML返回,您甚至可以检测用户是否已登录并显示相应的消息,或者如果数据检索出现错误,或者 whatever。您的JavaScript只需要处理获取。
async function requestData() { const element = document.querySelector(".content"); document.querySelector(".content").innerHTML = "loading..."; const response = await fetch("/api/endpoint/"); const html = await response.text(); document.querySelector(".content").innerHTML = content; } requestData();
在端点处您可以这样处理
<?php // "./api/endpoint/index.php" require "../vendor/autoload.php"; use Rossato/Element; use Something/Database; Database::connect(); function EndpointResponse($connected, $user) { if (!$connected) { return "Database could not be reached."; } if (!$user) { return "Log in please."; } return new Element("div", ["class" => "profile-name"], "Hello, " . $user->$name); } $connected = Database::connect(); $user = Database::getUser(); return EndpointResponse($connected, $user);
测试和平衡
关于声明式编程风格的思路是,您可以根据任意条件渲染不同的事物
if ($_SERVER['REMOTE_ADDR'] === "129.129.129.129") { echo new ThatNewFeatureComponent(); } else { echo new ProductionComponent(); }
或者,您可以设置缓存并将缓存提供给最终用户,为开发人员绕过它(但保持缓存文件完整)。
性能
由于我们在后端开发HTML结构,服务器CPU使用率将会增加,我认为大多数服务器的CPU使用率较低,内存延迟较高,如数据库调用和文件读取。
每次请求都会重新渲染整个结构,因此建议缓存结果(记忆化)。猜猜您怎么做
function saveCache($content) { file_put_contents($cacheFilename, strval($content)); } function loadCache() { return file_get_contents("./cache.html"); } function cacheExists() { return file_exists("./cache.html"); } function generate() { return App(); } if (cacheExists()) { echo loadCache(); } else { $content = generate(); saveCache($content); echo $content; } ```php # Licence You are free to use this and do what you want with it, like changing, redistributing, even claiming as your own, just don't annoy me as I provide no warranty or anything.