v2.5.2
Giriş yap

Php template engine mantığı nedir?

venloress
1,312 defa görüntülendi ve 1 kişi tarafından değerlendirildi

Merhaba laravel de gördüğümüz blade tema motorunun mantığı nedir?
Kaynak kodlarına baktım fakat pek birşey anlayamadım. Bir videoda tayfun hoca bunun gibi birşey deneriz belki demişti.
Böyle bir şeyin videosunu yaparsa harika olur.

Mantığı veya yapılışı nedir?

tayfunerbilen
1285 gün önce

mantığı aslında belli bir standartta yazmak ve mümkün olduğunca içinde saf php yazmak yerine direktifleri kullanarak ya da özel yapı oluşturarak kullanmak.
yani html kodlarını phpden ayırıyoruz. ayrıca tabi bazı avantajlarıda var örneğin blade engine'de layout sistemi oluşturmak çok kolay ve bunu extend ederek tek bir yerden kontrolünü sağlamak çok rahat.

yapılışı ise aslında tamamen kodları regex ile ayrıştırıp işlemekten ibaret, elbette çok kolay değil, her ihtimali düşünmek gerekiyor.

mesela birlikte bir örnek yapalım, yapacağımız template engine'de sadece değişkenleri ve foreach işlemlerini dönüştürelim.

dolayısı ile php dosyasında değişkeni yazdırırken <?php echo $degisken; ?> yerine {{ degisken }} şeklinde bir kullanım olsun ve foreach için şunun yerine;

<?php foreach($todos as $key => $todo): ?>
    <li>
        <?=$todo?>
    </li>
<?php endforeach; ?>

şöyle bir kullanımımız olsun

@foreach(todos as todo)
    <li>{{ todo }}</li>
@endforeach

bu mantıkta ilk olarak temel bir sınıfa ihtiyacımız var. kabaca oluşturduğum ve test ettiğim sınıfı yazayım;

<?php

// template.php

class TemplatEngine {

    public $view;

    public function view($view, $data = [])
    {
        extract($data);
        $viewName = $view . '.ptemplate.php';
        $viewPath = __DIR__ . '/views/' . $viewName;
        ob_start();
        require $viewPath;
        $this->view = ob_get_clean();
        $this->parse();
        
        $viewCachePath = __DIR__ . '/cache/' . md5($viewName) . '.cache.php';

        // cache yoksa oluştur
        if (!file_exists($viewCachePath)) {
            file_put_contents($viewCachePath, $this->view);
        }

        if (filemtime($viewCachePath) < filemtime($viewPath)) {
            file_put_contents($viewCachePath, $this->view);
        }

        require $viewCachePath;
        
    }

    public function parse()
    {
        $this->parseVariables();
        $this->parseForeach();
    }

    public function parseVariables()
    {
        $this->view = preg_replace_callback('/{{(.*?)}}/', function($variable) {
            return '<?=$' . trim($variable[1]) . '?>';
        }, $this->view);
    }

    public function parseForeach()
    {
        $this->view = preg_replace_callback('/@foreach\((.*?) as (.*?)\)/', function($variable) {
            if (strstr($variable[2], '=>')) {
                [$key, $value] = explode('=>', $variable[2]);
                return '<?php foreach($' . trim($variable[1]) . ' as $' . trim($key) . ' => $' . trim($value) . '): ?>';
            }
            return '<?php foreach($' . trim($variable[1]) . ' as $' . $variable[2] . '): ?>';
        }, $this->view);
        $this->view = preg_replace_callback('/@endforeach/', function($a){
            return '<?php endforeach; ?>';
        }, $this->view);
    }

}

ve klasör yapın şöyle olsun

cache/
views/
template.php
index.php

index.php de dosyalarımız;

<?php

require __DIR__ . '/template.php';

$template = new PrototurkTemplateEngine();

$template->view('index', [
    'title' => 'Prototürk',
    'name' => 'Tayfun Erbilen',
    'todos' => [
        'todo 1',
        'todo 2',
        'todo 3'
    ]
]);

views altında index.ptemplate.php adında bir dosyamız olacak ve içinde şunlar yazacak;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
</head>
<body>
    
    <h1>Hoşgeldin, {{ name }}</h1>

    <ul>
        @foreach(todos as todo)
            <li>
                {{ todo }}
            </li>
        @endforeach
    </ul>

    <ul>
        @foreach(todos as key => todo)
            <li>
                {{ todo }}
            </li>
        @endforeach
    </ul>

</body>
</html>

sonuç olarak cache/ klasöründe şöyle bir çıktı göreceksin;

// c9309029e1762f7da2afa048543c13b2.cache.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title><?=$title?></title>
</head>
<body>
    
    <h1>Hoşgeldin, <?=$name?></h1>

    <ul>
        <?php foreach($todos as $todo): ?>
            <li>
                <?=$todo?>
            </li>
        <?php endforeach; ?>
    </ul>

    <ul>
        <?php foreach($todos as $key => $todo): ?>
            <li>
                <?=$todo?>
            </li>
        <?php endforeach; ?>
    </ul>

</body>
</html>

tabi temelde bu çok basit bir örneğiydi, blade engine'deki gibi extend section push gibi direktiflerle çok daha kullanışlı hale getirilebilir.