Razões e problemas a serem resolvidos
Neste artigo, contarei como escrevi um gerador de código em php. Vou falar sobre o caminho que ele percorreu desde a geração de tabelas simples até um gerador bastante completo de código HTML e CSS. Vou dar exemplos de seu uso e mostrar projetos já concluídos.
Neste semestre, apenas o PHP poderia ser usado em uma das disciplinas.
Após o projeto interminável renomearO projeto foi denominado MelonPHP. Para as pessoas pensarem em comida quando dizem o nome dele? Mas nós temos um artigo aqui que não trata de gerar delírio, então deixe-me contar a você o motivo de sua criação.
Havia muito o que escrever, mas isso não é um problema. O principal problema era a saída de código HTML por meio de PHP. Vou tentar explicar o problema abaixo.
Por exemplo, aqui está a saída de texto por meio do eco familiar:
$text = "out text";
echo "<p>$text</p>";
Parece simples e direto. Vamos pegar um snippet do código do meu amigo onde ele gera uma tabela:
...
$sql = "SELECT * FROM table";$result = $conn->query($sql);
if($result->num_rows > 0) {
echo "<b>Table table</b><br><br>";
echo "<table border=2>";
echo "<tr><td> name </td>"."<td> name </td>"."<td> name </td></tr>";
while($row = $result->fetch_assoc()) {
echo "<tr><td>".$row["name"]."</td><td>".$row["name"]."</td><td>".$row["name"]."</td></tr>";
}
echo "</table>";
} else {
echo "0 results";}
...
isto Terrível o código demonstra os problemas que eu queria resolver:
- A presença de html no código php, o que o torna menos legível na minha opinião. Ainda assim, o arquivo de um yap deve conter o código de apenas um yap (s), na minha opinião
- Não há separação de lógica, tudo está uma bagunça. Eu queria uma interface de PHP melhor
É importante notar que tenho escrito no Flutter há relativamente muito tempo e realmente gosto da ideia por trás dele, relacionada a escrever uma interface através da construção de uma árvore a partir de widgets. Decidi pegar emprestada a ideia com nós (widgets) de lá.
, , .
. .
:
- UI / ( React)
- ( div, div, div, div...)
- UI PHP ( JS, HTML, CSS).
Rebuild callback , AJAX + JQuery- CSS ( , "width: 100px", PHP )
MelonPHP
- ( , ) 100%, .
- , , . ScrollView.
- .
, PHP , . ( , Microsoft MAUI, ).
MelonPHP Node. , 2 : Generate(), static Create().
- Generate() string — .
- Create() — . .
abstract class Node
{
abstract function Generate() : string;
static function Create() ...
}
Element
Element — , .
html .
, , .
Component
, , . ( ).
( MelonPHP), , , .
abstract class Component extends Element
{
function Initialize() ...
abstract function Build() : Element;
function Detach() ...
}
, ; Element. .
. ListItem, Component.
Initialize() Build().
Initialize() . .
Build() . . .
Detach() .
Build() , $Text.
Initialize() $Text .
Text(string) $Text.
$this , .
class ListItem extends Component
{
private $Text;
function Initialize() {
$this->Text = "Name";
}
function Build() : Element {
return Container::Create()
->Child(
Text::Create()
->Text($this->Text)
);
}
function Text(string $string) {
$this->Text = $string;
return $this;
}
}
DisplayComponent
DisplayComponent — , . Display.
.
Build() Document Title(string).
DisplayComponent, Build() Document. Document — , HTML5.
BuildList(), ListItem.
BuildList() . .
- , Builder. ...
Diplay(), , c .
class ListDisplay extends DisplayComponent
{
function Build() : Document {
return Document::Create()
//
->Title("test page")
->Child($this->BuildList());
}
function BuildList() {
$column = new Column;
for($i = 0; $i < 10; $i++)
$column->Children(
ListItem::Create()
->Text("number: $i")
);
return $column;
}
} ListDisplay::Display();
— .
Container
Container — , .
.
Column Row
. Child , Children .
Child Children .
Children , .
Children([Text::Create()]) Children(Text::Create())
Column — , .
CrossAlign MainAlign .
Row Column, .
Stack
Stack — , . , .
ScrollView, HorizontalScrollView, VerticalScrollView
.
HorizontalScrollView — .
VerticalScrollView — .
ScrollView — .
css .
css "background-color". php "". .
...
const BackgroundBlendMode = "background-blend-mode";
const BackgroundAttachment = "background-attachment";
const Border = "border";
const BorderSpacing = "border-spacing";
const BorderRadius = "border-radius";
const BorderImage = "border-image";
...
"34".Px. . css — Px(34). .
ThemeParameter(...). — , — /.
.
#f0f0f0.
. 20px, 15px.
. , CommaLine().
...
Container::Create()
->ThemeParameter(BackgroundColor, Hex("f0f0f0"))
->ThemeParameter(Padding, [Px(20), Px(15)]);
...
, (hover )? .
— css, media, keyframes, .
hover active.
ThemeBlock ThemeBlocks.
/ . my_container.
. : StandartModifier, HoverModifier, ActiveModifier. Parameter(...). Parameter ThemeParameter.
function GetMyTheme() : Theme {
return Theme::Create()
->ThemeBlocks([
ThemeBlock::Create()
->Keys("my_container")
->Modifiers([
StandartModifier::Create()
->Parameter(BackgroundColor, Red)
->Parameter(Padding, [Px(10), Px(12)]),
HoverModifier::Create()
->Parameter(BackgroundColor, Green),
ActiveModifier::Create()
->Parameter(BackgroundColor, Blue)
])
]);
}
( css) ThemeKeys. Themes.
class TestThemeDisplay extends DisplayComponent
{
function Build() : Document {
return Document::Create()
->Themes(GetMyTheme())
->Child(
Container::Create()
->ThemeKeys("my_container")
);
}
} TestThemeDisplay::Display();
.
keyframes.
keyframe, FrameBlocks.
FrameBlock.
FrameBlock Frames. , Value. ( Pr(value)) From To.
, .
, shake_text.
, .
, .
function GetMyTheme() : Theme {
return Theme::Create()
->ThemeBlocks([
ThemeBlock::Create()
->Keys("my_container")
->Modifiers([
StandartModifier::Create()
->Parameter(Padding, [Px(10), Px(12)]),
HoverModifier::Create()
->Parameter(BackgroundColor, Green),
ActiveModifier::Create()
->Parameter(BackgroundColor, Blue)
]),
ThemeBlock::Create()
->Keys("shake_text")
->Modifiers([
StandartModifier::Create()
->Parameter(Color, Red)
->Parameter(Animation, ["shake_text_anim", ".2s", "ease-in-out", "5", "alternate-reverse"])
])
])
->FrameBlocks(
FrameBlock::Create()
->Key("shake_text_anim")
->Frames([
Frame::Create()
->Value(Pr(0))
->Parameter(Transform, Translate(0, 0)),
Frame::Create()
->Value(Pr(25))
->Parameter(Color, Hex("ff4040"))
->Parameter(Filter, Blur(Px(0.5))),
Frame::Create()
->Value(Pr(50))
->Parameter(Filter, Blur(Px(1.2))),
Frame::Create()
->Value(Pr(75))
->Parameter(Color, Hex("ff4040"))
->Parameter(Filter, Blur(Px(0.5))),
Frame::Create()
->Value(Pr(100))
->Parameter(Transform, Translate(Px(10), 0)),
])
);
}
class TestThemeDisplay extends DisplayComponent
{
function Build() : Document {
return Document::Create()
->Themes(GetMyTheme())
->Child(
Container::Create()
->ThemeKeys("my_container")
->Child(
Text::Create()
->ThemeKeys("shake_text")
->Text("Error text")
)
);
}
} TestThemeDisplay::Display();
2 . , . : MinWidth, MaxWidth, MinHeight, MaxHeight, .
MinWidth 800px.
MaxWidth 800px.
, — . adaptive_color.
.
.
function GetMobileTheme() : Theme {
return Theme::Create()
->MinWidth(Px(800))
->ThemeBlocks(
ThemeBlock::Create()
->Keys("adaptive_color")
->Modifiers(
StandartModifier::Create()
->Parameter(BackgroundColor, Green)
)
);
}
function GetDesktopTheme() : Theme {
return Theme::Create()
->MaxWidth(Px(800))
->ThemeBlocks(
ThemeBlock::Create()
->Keys("adaptive_color")
->Modifiers(
StandartModifier::Create()
->Parameter(BackgroundColor, Red)
)
);
}
class TestThemeDisplay extends DisplayComponent
{
function Build() : Document {
return Document::Create()
->Themes([
GetMyTheme(),
GetDesktopTheme(),
GetMobileTheme()
])
->Child(
Container::Create()
->ThemeKeys(["my_container", "adaptive_color"])
->Child(
Text::Create()
->ThemeKeys("shake_text")
->Text("Error text")
)
);
}
} TestThemeDisplay::Display();
.
DisplayComponent.
Build() Document.
class ClickerDisplay extends DisplayComponent
{
function Build() : Element {
return Document::Create()
->Title("Clicker");
}
} ClickerDisplay::Display();
.
.
class ClickerDisplay extends DisplayComponent
{
function Build() : Element {
return Document::Create()
->Title("Clicker")
->Child(
Column::Create()
->Children([
Text::Create()
->Text("Pressed 0 times"),
Button::Create()
->Text("Press")
])
);
}
} ClickerDisplay::Display();
.
ThemeParameter, .
class ClickerDisplay extends DisplayComponent
{
function Build() : Element {
return Document::Create()
->Title("Clicker")
->Child(
Column::Create()
->ThemeParameter(Padding, Px(15))
->Children([
Text::Create()
->ThemeParameter(PaddingBottom, Px(15))
->Text("Pressed 0 times"),
Button::Create()
->ThemeParameter(Width, Auto)
->ThemeParameter(Padding, [Px(4), Px(10)])
->ThemeParameter(BackgroundColor, Blue)
->ThemeParameter(Color, White)
->ThemeParameter(BorderRadius, Px(4))
->Text("Press")
])
);
}
} ClickerDisplay::Display();
.
.
Initialize() TapCount.
form — Action.
Action . Action Post. , .
click_count Action. TapCount.
Initialize() Action::GetValue(name, standart_value, action_type) . 0, Post.
.
Imprima " Pressione $ this-> TapCount vezes " no texto .
É isso, um simples clicker está pronto.
class ClickerDisplay extends DisplayComponent
{
private $TapCount;
function Initialize() {
$this->TapCount = Action::GetValue("click_count", 0 /* standart value */, ActionTypes::Post);
$this->TapCount++;
}
function Build() : Document {
return Document::Create()
->Title("Test page")
->Child(
Action::Create()
->Type(ActionTypes::Post)
->Variable("click_count", $this->TapCount)
->Child(
Column::Create()
->ThemeParameter(Padding, Px(15))
->Children([
Text::Create()
->ThemeParameter(PaddingBottom, Px(15))
->Text("Press $this->TapCount times"),
Button::Create()
->ThemeParameter(Width, Auto)
->ThemeParameter(Padding, [Px(4), Px(10)])
->ThemeParameter(BackgroundColor, Blue)
->ThemeParameter(Color, White)
->ThemeParameter(BorderRadius, Px(4))
->Text("Press")
])
)
);
}
} ClickerDisplay::Display();
Resultado
Consegui escrever um gerador de código simples, mas poderoso o suficiente.
Ele passou da geração de tabelas simples para um gerador de html e css completo, no qual você pode fazer o layout de projetos de forma conveniente e combinar o layout com a lógica.
Neste framework, escrevi um projeto de curso (screenshots abaixo), usei-o no exame e fiz trabalhos de estudo sobre ele.