PHP - "Template Engine"

Liittynyt
18.11.2019
Viestejä
74
Moi,
heti alkuun haluaisin sanoa että en ole ohjelmistokehityksen ammattilainen. Leipätyökseni seilaan maailman meriä, joka kaiketi näkyy koodissa. Jokatapauksessa, mulla olis pari kysymystä teille, oikeille ammattilaisille.

Eli siis, mulla on pienimuotoinen nettisivuprojekti, ja ihan oppimistarkoituksessa oon lähtenyt kokeilemaan, että miten tällänen ns. "Template Engine" toimii ja millaisia mahdollisuuksia se tarjoaa. En ole vertaamassa / vertaa tätä mihinkään oikeaan template engineen, kuten Twig ym.

Mulla on siis yksinkertainen ajatus tän suhteen, luon yksinkertaisen classin, joka lataa .html tiedoston sisällön (about näin)

PHP:
//construct method
if(!empty($template_file)) {
    if(file_exists($template_file)) {
        $this->template = file_get_contents($template_file);
    }
}
Eli, tää siis ensin varmistuu että sille on annettu toi template, ja jos se annettu template löytyy määritetyn polun päästä hakee sen template tiedoston sisällön.
Tämän jälkeen, loin methodin, joka etsii tuosta haetusta tiedostosta korvattavan syntaxin, sanotaan vaikka { VAR }, korvaa sen annetulla arvolla.

PHP:
public function pass($toReplace, $replaceWith) {
if(!empty($toReplace)) {
    $this->template_data[strtoupper($toReplace)] = htmlspecialchars($replaceWith);
    }
}
Ja tämän jälkeen sitten mulla on render -method, joka sit puolestaan renderöi uuden tiedoston.
PHP:
public function render() {
    if(count($this->template_data) > 0) {
        foreach($this->template_data as $var => $tpl_data) {
            $this->template = str_replace('{'.$var.'}', $tpl_data, $this->template);
        }
    }
    echo $this->template;
    }
Eli mua kiinnostaisi kuulla muiden mietteitä tästä, missä menee vikaan, mitä teen turhaan, jne. Ai niin ja millaisia turvallisuusriskejä tälläinen lähestymistapa luo?
Uskon itse, että tää kestää pienen - keskisuuren trafiikin ihan ongelmitta, mutta kovassa ruuhkassa voi jäädä jalkoihin (?) Ensin mietin että pitäiskö template-tiedosto cachettaa, mutta lopputulema johon tulin on seuraava;
- Sivusto tarjoilee dynaamista dataa, joten cachettelu ei taida oikein olla vaihtoehto search, replace -> cache -> render välissä(?)
- Miksi hitossa mä cachettelisin tekstitiedostoja ( cache -> search, replace -> render)

Kiitos, ja kumarrus, toivottavasti edes joku ymmärtää :D
 

SPu

Liittynyt
20.10.2016
Viestejä
128
Perusideana - pikkusivustolle ei kannata liian monimutkaista tehdä, mutta varmistu siitä että ladattavan templaten tiedostonimeä ei voi syöttää käyttäjän puolelta mitenkään (siis $template_file sun esimerkissä). Eli luo vaikkapa lista (tietokanta tai tiedosto) jossa tietty sivuosoite (esim /Login/) yhdistetään tiettyyn templatetiedoston (esim. /templates/login.php). Jos koodi yrittää arvata templatea vain tutkimalla onko tiedosto olemassa, niin siinä on riski päästä käsiksi asioihin jotka ei käyttäjälle kuulu (eli esim jos avaat sivun /pahasivu.php, niin template-engine avaa /templates/pahasivu.php ilman mitään tarkistusta, niin seuraavaksi kokeilen sitten ../../etc/htpasswd jne.jne).

Jos taustakoodi ei tee mitään erityisen raskasta ja sisältö on aina dynaamista, niin cache ei anna merkittävää etua nykyraudalla. Asia on eri jos puhutaan satojen tuhansien - miljoonien sivulatausten kokoluokasta päivittäin. Mutta tuntematta datan sisältöä tuohon ei voi oikein antaa yleispätevää vastausta. Voi olla myös fiksua cachettaa vain osa sivusta (esim. valikot/navigaatio, jos ne kasataan dynaamisesti). Hyvin simppelin cachesysteemin itse koodaus ei ole mikään monimutkainen asia, tässä nopeasti googlattu esimerkki: How to Create a Simple and Efficient PHP Cache - DZone Performance

Olen tuollaisen vastaavalla "simppeli template engine" -idealla toimivan sivustopohjan koodannut 10v+ sitten ja sitä ajan mittaan laajentanut, ja se on ollut käytössä kymmenillä nettisivuilla omissa projekteissa (ei megaisoja sivustoja, parhaimmat luokkaa 5-10 mil sivulatausta per kk), ja tein kesällä siitä täysin päivitetyn version samalla keep-it-simple -logiikalla. Tutkailin myös valmiita frameworkkeja, mutta ovat kovin raskaita ja monimutkaisia haluttuun tarkoitukseen mun osalta.
 
Viimeksi muokattu:
Liittynyt
18.11.2019
Viestejä
74
Perusideana - pikkusivustolle ei kannata liian monimutkaista tehdä, mutta varmistu siitä että ladattavan templaten tiedostonimeä ei voi syöttää käyttäjän puolelta mitenkään (siis $template_file sun esimerkissä). Eli luo vaikkapa lista (tietokanta tai tiedosto) jossa tietty sivuosoite (esim /Login/) yhdistetään tiettyyn templatetiedoston (esim. /templates/login.php). Jos koodi yrittää arvata templatea vain tutkimalla onko tiedosto olemassa, niin siinä on riski päästä käsiksi asioihin jotka ei käyttäjälle kuulu (eli esim jos avaat sivun /pahasivu.php, niin template-engine avaa /templates/pahasivu.php ilman mitään tarkistusta, niin seuraavaksi kokeilen sitten ../../etc/htpasswd jne.jne).

Jos taustakoodi ei tee mitään erityisen raskasta ja sisältö on aina dynaamista, niin cache ei anna merkittävää etua nykyraudalla. Asia on eri jos puhutaan satojen tuhansien - miljoonien sivulatausten kokoluokasta päivittäin. Mutta tuntematta datan sisältöä tuohon ei voi oikein antaa yleispätevää vastausta. Voi olla myös fiksua cachettaa vain osa sivusta (esim. valikot/navigaatio, jos ne kasataan dynaamisesti). Hyvin simppelin cachesysteemin itse koodaus ei ole mikään monimutkainen asia, tässä nopeasti googlattu esimerkki: How to Create a Simple and Efficient PHP Cache - DZone Performance

Olen tuollaisen vastaavalla "simppeli template engine" -idealla toimivan sivustopohjan koodannut 10v+ sitten ja sitä ajan mittaan laajentanut, ja se on ollut käytössä kymmenillä nettisivuilla omissa projekteissa (ei megaisoja sivustoja, parhaimmat luokkaa 5-10 mil sivulatausta per kk), ja tein kesällä siitä täysin päivitetyn version samalla keep-it-simple -logiikalla. Tutkailin myös valmiita frameworkkeja, mutta ovat kovin raskaita ja monimutkaisia haluttuun tarkoitukseen mun osalta.
Isosti kiitos kommentista!
Tällä hetkellä käytän tätä seuraavanlaisesti;
PHP:
//autoload.php sisältää DEFINE TEMPLATE_PATH hommelit
$template = new Template(TEMPLATE_PATH.'/index.tpl');
$esimerkki = 'jotain tekstiä';
$template->pass('varru', $esimerkki);
$template->render();
Kun teen tämän näin, eihän mulla periaateessa ole hätää, en muutenkaan mielelläni käytä $_GET superglobalia ihan siitä syystä, että menee helposti homma niin sanotusti vituiksi.
 

EKM

Liittynyt
05.02.2021
Viestejä
4
Valitsemallasi tavalla template-enginen kehittely voisi olla ihan hauskaa puuhaa, mutta riippuen kuinka monimutkaisia asioita sen pitäisi kyetä tekemään, saattaa olla paikallaan käyttää joko markkinoilta löytyviä valmiita koneita tai renderöidä php-tiedostoja suoraan. Jos ajatus on vain korvata tekstiä toisella tekstillä, lähestymistapasi ajaa varmasti asian. Pikkutarpeissa valmiit enginet ovatkin hiukka overkill. Mutta jos pitäisi esimerkiksi iteroida listoja läpi, lähestymistapasi voi olla jo astetta haastavampi. Tietysti on mahdollista renderöidä kehitelty notaatio php-koodiksi, tallentaa se tiedostoon ja renderöidä sitten syntynyt php-tiedosto, jolloin monimutkaisiakin ajatuksia voisi ehkä toteuttaa pelkällä tekstinotaatiolla vieläpä suhteellisen vähäisellä investoinnilla. Olen tosin pikkutarpeisiin itse renderöinyt normaalin php sivun ja tulostanut sen näytölle. Tämä antaa suoraan kaiki php:n tuomat ominaisuudet ilman, että kehittelisin oman notaation ja sille toteutuksen.

Olen siis tehnyt pikkutarpeisiin php-sivun ihan normaalina html-koodina, jossa php-koodi mukana. Tyyliin:

PHP:
<!-- /var/www/kotisivut/views/users/index.php -->
<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">
  <title><?php echo $head_title; ?></title>
  <link rel="stylesheet" href="<?php echo $base_url; ?>/css/styles.css?v=1.0">
</head>

<body>
  <table>
    <thead>
      <tr>
        <th>Id</th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <?php foreach($users as $user) { ?>
        <tr>
          <td><?php echo $user['id']; ?></td>
          <td><?php echo $user['name']; ?></td>
        </tr>
      <?php } ?>
    </tbody>
  </table>
  <script src="<?php echo $base_url; ?>/js/<?php echo $js_file; ?>.js"></script>
</body>
</html>
Sitten alustanut muuttujat, rakentanut html-sivun ja tulostanut sen:

PHP:
/* /var/www/kotisivut/public/index.php */
if(!defined('DS')) {
    define('DS', DIRECTORY_SEPARATOR);
}
include_once(dirname(__DIR__) . DS . 'renderers' . DS . 'Renderer.php');

$params = [
    'head_title' => 'Kotisivut | Käyttäjät',
    'base_url' => 'https://www.kotisivut.com',
    'js_file' => 'users',
    'users' => [
        [
            'id' => 1,
            'name' => 'Tero Testaaja'
        ],
        [
            'id' => 2,
            'name' => 'Mikko Mallikas'
        ],
        [
            'id' => 3,
            'name' => 'Essi Esimerkki'
        ],
    ],
];

echo view('users.index', $params)->render();
Oletetaan, että yo. php-tiedosto on "index.php" ja sijaitsee polussa "/var/www/kotisivu/public/index.php" (oletetaan myös, että domaini osoittaa tuohon public-kansioon, jotta toteutus pysyy netin ulkopuolella) ja html-template-tiedosto nimeltään "index.php" ja sijaitsee polussa "/var/www/kotisivu/views/users/index.php" ja alla oleva templatenrenredöintiluokka polussa "/var/www/kotisivu/renderers/Renderer.php", palauttaa se sivun php-muuttujat html-koodiksi renderöitynä:

PHP:
/* /var/www/kotisivut/renderers/Renderer.php */

if(!defined('DS')) {
    define('DS', DIRECTORY_SEPARATOR);
}

function view($view, $args) {
    $r = new Renderer();
    $r->args = $args;
    $view = str_replace('.', DS, $view);
    $r->path = dirname(__DIR__) . DS . 'views' . DS . $view . '.php';
    return $r;
}

class Renderer {
    public $args;
    public $path;
    public function render(){
        ob_start();
        extract($this->args);
        include($this->path);
        $var=ob_get_contents();
        ob_end_clean();
        return $var;
    }
}
Yo. implementaatio lienee osaksi pseudokoodia. En ole nimittäin kokeillut, että juuri tämä versio toimisi. Ajatus on kuitenkin tuo, eli:
  1. teen validin php-tiedoston, jossa php koodi upotettuna html sekaan.
  2. alustan php-muuttujat ja välitän ne renderöivälle prosessille joka renderöi php-tiedoston niin, että nuo alustamani php-muuttujat ovat käytössä.
  3. tulostan renderöidyn tekstin näytölle.
 
Viimeksi muokattu:
Toggle Sidebar

Statistiikka

Viestiketjut
239 412
Viestejä
4 194 560
Jäsenet
70 728
Uusin jäsen
pvarjone

Hinta.fi

Ylös Bottom