Comment j'ai écrit un générateur de code en PHP et ce qui en est arrivé

Raisons et problÚmes à résoudre



Dans cet article, je vais vous expliquer comment j'ai écrit un générateur de code en php. Je vais vous parler du chemin qu'il est passé de la génération de tableaux simples à un générateur assez complet de code html et css. Je vais donner des exemples de son utilisation et montrer des projets déjà achevés.



Ce semestre, seul PHP pouvait ĂȘtre utilisĂ© dans l'une des matiĂšres.



AprÚs le renommage du projet sans finLe projet a été nommé MelonPHP. Pour que les gens pensent à la nourriture quand ils disent son nom? Mais nous avons ici un article non pas sur la génération du délire, alors laissez-moi vous expliquer la raison de sa création.



Il y avait beaucoup Ă  Ă©crire, mais ce n'est pas un problĂšme. Le principal problĂšme Ă©tait la sortie du code HTML via PHP. Je vais essayer d'expliquer le problĂšme ci-dessous.



Par exemple, voici la sortie de texte via l'Ă©cho familier:



$text = "out text";
echo "<p>$text</p>";


Cela semble simple et direct. Prenons un extrait du code de mon ami oĂč il gĂ©nĂšre une table:



...
$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";}
...


il terrible le code montre les problÚmes que je voulais résoudre:



  • La prĂ©sence de html dans le code php, ce qui le rend moins lisible Ă  mon avis. Pourtant, le fichier pour un yap devrait contenir le code d'un seul yap (s), Ă  mon avis
  • Il n'y a pas de sĂ©paration de la logique, tout est en dĂ©sordre. Je voulais une interface PHP plus agrĂ©able


Il est intĂ©ressant de noter que j'Ă©cris dans Flutter depuis relativement longtemps et que j'aime vraiment l'idĂ©e derriĂšre cela, liĂ©e Ă  l'Ă©criture d'une interface en construisant un arbre Ă  partir de widgets. J'ai dĂ©cidĂ© d'emprunter l'idĂ©e avec des nƓuds (widgets) Ă  partir 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.



.



Imprimer " Press $ this-> TapCount times " dans le texte .



VoilĂ , un simple clicker est prĂȘt.



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();




RĂ©sultat



J'ai réussi à écrire un générateur de code simple mais assez puissant.



Il est passé de la génération de tableaux simples à un générateur html et css à part entiÚre, sur lequel vous pouvez facilement mettre en page des projets et combiner la mise en page avec la logique.



Sur ce cadre, j'ai écrit un projet de cours (captures d'écran ci-dessous), je l'ai utilisé dans l'examen et y ai étudié des devoirs.



Captures d'écran du projet de cours réalisé sur MelonPHP







Sources



GitHub - MelonPHP



Battement



MAUI




All Articles