rossato/declarative-php

PHP构建声明式和静态Web应用的微框架

v1.1 2019-07-12 03:46 UTC

This package is auto-updated.

Last update: 2024-09-15 12:52:58 UTC


README

Declarative PHP

🔨 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属性在两个方面都有很强的特性

  1. 如果您在该属性中放入一个对象,它将成为该对象的字符串表示形式
$style = [
    "width" => 200,
    "height" => 200,
    "background-color" => "red"
];
echo new Element("div", ["style" => $style]);;
<div style="width:200px,height:200px,background-color:red"/>
  1. 但如果您放入一个原始字符串,我们将通过一个简化的函数来最小化它,以节省一些字节

```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 &gt;div&lt;&gt;div&lt');</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.