[php] Variabelen vervangen in template

Pagina: 1
Acties:
  • 382 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Ik heb een html template waarin ik variabelen heb staan tussen { } die ik met een php script wil vervangen door de waarde ervan. Het vervangen lukt niet. Wat doe ik fout?

De template is:
HTML:
1
2
3
4
5
6
<html><head><title>{title}</title></head>
<body>
<h3>Hello {name}</h3>
<p>The time is: {datetime} </p>
</body>
</html>


Het script is:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

$contents = file_get_contents("template.html");
$title = "Titel van de pagina";
$name = "Henk";
$datetime = date("Y-d-m H:m:s");

//$contents = str_replace("{".title."}", $title, $contents);
//$contents = str_replace("{".name."}", $name, $contents);
//$contents = str_replace("{".datetime."}", $datetime, $contents);

$find = "/{(.*?)}/";
$repl = '${1};';

$contents = preg_replace($find, $repl, $contents);

echo $contents;
?>


Zoals je ziet, kan het wel met str_replace (hetgeen nu uit gecommentarieerd is), maar volgens mij moet het in een keer kunnen met preg_replace.

Zoals het er nu staat wordt het resultaat "Hello name" in plaats van "Hello Henk".
Wat ik ook geprobeerd heb is preg_replace($find, eval("echo ".$repl.";", $contents)
maar dat geeft lege strings.

Acties:
  • 0 Henk 'm!

Verwijderd

Wat je nu doet (en uit hebt gecommentarieerd) is niet geheel goed
PHP:
1
//$contents = str_replace("{".title."}", $title, $contents);

Ok, 1 hint dan. Je haalt een variabele buiten de quotes {".title."} (tenminste, dat probeer je) maar dat doe je op dit moment met title. title is geen PHP variabele. $title is een PHP variabele.
Als je een echo doet, dan zou je een variabele wel buiten quotes kunnen halen:
PHP:
1
echo "hallo ".$title." dit is een test";

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:47

Creepy

Tactical Espionage Splatterer

Oftewel: echo eens wat je nu aan je normale replace meegeeft, dan zul je zien dat je helemaal niet zoekt op {title}, {name} en {datetime}. En als je je errorreporting op E_ALL had gezet dan had je ook een notice of warning gekregen.

Overigens verwachten we hier eigenlijk van je dat je dit soort zaken zelf al hebt geprobeerd en dat je ook aangeeft wat je nu geprobeerd hebt en wat er niet lukte. Nu laat je wat code zien met de melding "het werkt niet, hoe moet het wel" en dat is eigenlijk niet voldoende. Let hier AUB in het vervolg op.

[ Voor 40% gewijzigd door Creepy op 21-11-2007 15:54 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Verwijderd schreef op woensdag 21 november 2007 @ 15:50:
Wat je nu doet (en uit hebt gecommentarieerd) is niet geheel goed
PHP:
1
//$contents = str_replace("{".title."}", $title, $contents);
Dit werkt wel, probeer maar.
Het is helaas niet wat ik wil. Ik wil in één keer vervangen omdat ik later een template maak met veel meer variabelen, en de boel in een class ga gooien.

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:47

Creepy

Tactical Espionage Splatterer

Ook goed :)

Blijft mijn vraag staan: Wat heb je zelf al geprobeerd en wat lukte daar niet mee? En waarom moet dit perse in 1 regel code?

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Creepy schreef op woensdag 21 november 2007 @ 15:56:
Ook goed :)

Blijft mijn vraag staan: Wat heb je zelf al geprobeerd en wat lukte daar niet mee? En waarom moet dit perse in 1 regel code?
Ok, iets meer toelichting.
De code die ik de variabelen wil laten vervangen, zal in principe niet weten welke variabelen er op de template staan, daarom moet het in één keer. Hij moet ze alleen vervangen door de waarde, dus elke {var} moet vervangen worden door de waarde van $var.

Ik heb ook het volgende geprobeerd:
PHP:
1
2
3
$reg = "/{(.*?)}/";
preg_match_all($reg, $contents, $arr);
print_r($arr);

om te kijken of de search mbv. de reguliere expressie goed gaat, en dat is het geval.
Ik krijg er een array uit met drie matches. Er staan ook drie variabelen in deze template.

Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 22:34
Waarom type je niet gewoon <?=$var?> in je template?

Anyway, als ik je het per se met accolades enzo wil oplossen zou ik kijken naar preg_replace_callback en de mogelijkheden van de $GLOBALS superglobal

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
probeer zoiets:

SQL:
1
2
3
4
5
6
7
8
function blaat(varname) {
    // eerst nog ff globaal maken
   ....
    // en dan
    return ${$varname};
}

$result = preg_replace(array("/\{(.*)\}/e"), array("blaat(\\1)") , $result) ;

Acties:
  • 0 Henk 'm!

  • Raynman
  • Registratie: Augustus 2004
  • Laatst online: 01:02
Zoals gezegd is de str_replace die je gebruikt om te testen niet goed, je zet de string deels netjes tussen aanhalingstekens en deels niet. Nogal onlogisch. Als je notices uit hebt staan merk je er niks van: zolang er geen constante 'name' of 'title' bestaat, wordt aangenomen dat het een string had moeten zijn.

str_replace kun je prima gebruiken; je hoeft de namen van de variabelen/placeholders niet hard-coded in je script op te nemen. De gebruikelijke manier van werken is in een array bijhouden welke placeholder vervangen moet worden door welke waarde (placeholder als key in associatieve array bij de waarde waardoor hij vervangen moet worden). Je krijgt dan vaak zoiets (vaak gebruikt men een klasse):
PHP:
1
2
3
<?php
$template->set('name', 'Henk');
?>

De memberfunctie set voegt een element aan een array toe, zeg $values, en dan kan je met een foreach parsen of misschien als volgt:
PHP:
1
2
3
<?php
$output = str_replace(array_keys($values), $values, $template_contents);
?>


Dan even naar je reguliere expressie. Volgens je test matcht hij wel; zou ook moeten. Kijk echter eens goed naar $find en $repl. Je vervangt accolades en alles wat daartussen gevonden wordt door hetgeen wat tussen de accolades stond. Geschreven als losse str_replace-calls wordt het zoiets:
PHP:
1
2
3
$template_contents = str_replace('{name}', 'name', $template_contents);
$template_contents = str_replace('{title}', 'title', $template_contents);
//enz.


Wat je volgens mij wilt is in $repl een variabele variabele gebruiken: {name} moet vervangen worden door de waarde van $name. Met het volgende kan dat misschien (let op e-modifier), maar ik zou zelf waarschijnlijk toch kiezen voor eerder genoemde oplossing met str_replace (weet niet eens of variabele var hieronder werkt, is sowieso geen mooie oplossing).
PHP:
1
2
$find = '/{(.*?)}/e';
$repl = '$${1};';


Edit: of mijn versie werkt weet ik dus niet zeker, die van Edwardvb zal waarschijnlijk wel werken als je de parameters voor preg_replace niet tot een array maakt.

[ Voor 4% gewijzigd door Raynman op 21-11-2007 16:30 ]


Acties:
  • 0 Henk 'm!

Verwijderd

dik_voormekaar schreef op woensdag 21 november 2007 @ 15:54:
[...]

Dit werkt wel, probeer maar.
Het is helaas niet wat ik wil. Ik wil in één keer vervangen omdat ik later een template maak met veel meer variabelen, en de boel in een class ga gooien.
Ok, het zal best werken, maar het bleeft een bad practice. Wat er nu namelijk gebeurt is dat php je title (zonder $ ervoor) vervangt door de string "title". Het zal dus wel werken, maar wat je bedoelt is "{title}". Ook al kan het met str_replace, ik zou alle variabelen die je wilt vervangen in een arrary zitten en foreach gebruiken.

PHP:
1
2
3
4
$te_vervangen = array("title","nogiets");
foreach($te_vervangen as $key){
  $contents = str_replace("{".$key."}",$$key,$contents);
}

Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Edwardvb schreef op woensdag 21 november 2007 @ 16:13:
probeer zoiets:

SQL:
1
2
3
4
5
6
7
8
function blaat(varname) {
    // eerst nog ff globaal maken
   ....
    // en dan
    return ${$varname};
}

$result = preg_replace(array("/\{(.*)\}/e"), array("blaat(\\1)") , $result) ;
Helaas, dit geeft hetzelfde resultaat als dat ik al had via :
PHP:
1
$contents = preg_replace($find, eval("echo ".$repl.";"), $contents);

namelijk lege strings (Er staat dus niets achter Hello...)
Ik weet dat de $find resultaat geeft, want die heb ik al getest via preg_match_all(),
dus het moet liggen aan de replacements.

Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Ik ben eruit dankzij diverse tips hier !!

Het resultaat moest zijn:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

$contents = file_get_contents("template.html");
$title = "Titel van de pagina";
$name = "Henk";
$datetime = date("Y-d-m H:m:s");

$find = "/\{(.*)\}/e";
$repl = '$${1}';

//preg_match_all($find, $contents, $arr);
$contents = preg_replace($find, $repl, $contents);

echo $contents;
?>

Bedankt allemaal.

Acties:
  • 0 Henk 'm!

  • BCC
  • Registratie: Juli 2000
  • Laatst online: 22:08

BCC

Waarom gebruik je hier geen template engine voor? Dit is verre van efficient.

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


Acties:
  • 0 Henk 'm!

  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
BCC schreef op woensdag 21 november 2007 @ 22:20:
Waarom gebruik je hier geen template engine voor? Dit is verre van efficient.
Waarom is het verre van efficient?
Een template engine is denk ik wat groot en log voor zoiets (nog) vrij eenvoudigs.

Acties:
  • 0 Henk 'm!

  • MBV
  • Registratie: Februari 2002
  • Laatst online: 20-09 22:44

MBV

Ten eerste: voor wat je er nu van gebruikt, is PHP net zo handig. Maak van je Templates simpelweg .php-bestanden, en zet overal waar een variabele moet komen <?=$var?>. Include het template op de plek waar je nu je moeilijke template-ding doet, en je bent klaar.

Ten tweede: als je dan toch een ander template-systeem wilt dan PHP, wat is er mis met een groot systeem gebruiken voor zoiets? Pak-em-beet Smarty (zonder een holy war uit te willen lokken) kan dit prima, en snel doen. Waarschijnlijk zelfs sneller: als je Smarty goed instelt wordt aan caching gedaan, zodat een bijna statische pagina maar 1x wordt gegenereerd :)

Acties:
  • 0 Henk 'm!

  • Borizz
  • Registratie: Maart 2005
  • Laatst online: 24-08 20:35
Het PEAR package HTML_Template_Sigma is denk ik een prima oplossing voor jou. Je kan zelfs de syntax gebruiken die je nu al gebruikt, met nog een aantal extra opties meer en het is sneller opgezet dan Smarty (waar dan wel weer meer mee mogelijk is).

If I can't fix it, it ain't broken.


  • BCC
  • Registratie: Juli 2000
  • Laatst online: 22:08

BCC

dik_voormekaar schreef op woensdag 21 november 2007 @ 22:35:
[...]
Waarom is het verre van efficient?
Een template engine is denk ik wat groot en log voor zoiets (nog) vrij eenvoudigs.
Omdat hij nu voor elke variable je volledige content door moet regexpen. In dit voorbeeld dus al 3x.

Na betaling van een licentievergoeding van €1.000 verkrijgen bedrijven het recht om deze post te gebruiken voor het trainen van artificiële intelligentiesystemen.


  • SchizoDuckie
  • Registratie: April 2001
  • Laatst online: 18-02 23:12

SchizoDuckie

Kwaak

Ik doe eigenlijk op sommige plekken exact hetzelfde als jij, maar dan zonder de overhead:
Ter lering ende vermaeck:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?


class TemplateEngine
{
    var $template, $templateValues, $availableValues;
    function __construct($template='')
    {   
        $this->template = $template;
        $this->availableValues = array();
        $this->templateValues = array();

    }
    
    function __set($key, $value)
    {
        $this->availableValues[$key] = $value;
    }

    function feedValues($input)
    {
        foreach ($input as $key=>$val)
        {
            $this->availableValues[$key] = $val;
        }
    }

    function loadTemplate($template)
    {
        $this->template = $template;
    }

    function grabValues($input)
    {
        preg_match_all('!\{(.*)\}!U', $input, $output);
        return($output);
    }

    function run()
    {
        $tpl = file_get_contents($this->template);
        $this->templateValues = $this->grabValues($tpl);
        for ($i=0; $i<sizeof($this->templateValues[1]); $i++)
        {
            $tpl = str_replace($this->templateValues[0][$i], stripslashes($this->availableValues[$this->templateValues[1][$i]]), $tpl);
        }

        return ($tpl);  

    }

}

usage:

PHP:
1
2
3
4
$template = new TemplateEngine('bestandje.tpl');
$template->test = 'woei';
$template->blaat = 'mekker';
$output = $template->run(); // vervangt {test} door woei en {blaat} door mekker


overigens voor de mensen die vragen naar 'waarom geen <?=$var;?>'
omdat sommige templates door totale nitwits aangepast aangepast moeten kunnen worden, en je die niet wil lastig vallen met zulke 'enge' dingen :')

[ Voor 7% gewijzigd door SchizoDuckie op 22-11-2007 10:57 ]

Stop uploading passwords to Github!


  • dik_voormekaar
  • Registratie: April 2003
  • Laatst online: 15-09 21:32
Het staat hier inderdaad wel mooi in een template, maar in principe doe je precies hetzelfde als ik,
namelijk voor elke variabele die in template staat, een str_replace. Dus je gaat ook meerdere keren je hele content vervangen.

[ Voor 14% gewijzigd door dik_voormekaar op 22-11-2007 11:11 ]


  • PrisonerOfPain
  • Registratie: Januari 2003
  • Laatst online: 26-05 17:08
SchizoDuckie schreef op donderdag 22 november 2007 @ 10:55:
overigens voor de mensen die vragen naar 'waarom geen <?=$var;?>'
omdat sommige templates door totale nitwits aangepast aangepast moeten kunnen worden, en je die niet wil lastig vallen met zulke 'enge' dingen :')
Zelf heb ik dat opgelost door eenvoudigere constructies aan te bieden voor PHP files.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Simple class that alows me to read the templates as a special stream (view://).
//
// Advantages are: 
//
// - Not having to worry about short_open_tags as we handle that manually.
// - Shorter syntax for accessing variables (@$some_variable for $this->some_variable).
// - Shorter syntax for accessing methods (@->some_method() for $this->some_method()).
// - Shorter syntax for embedding chunks ~('name.of.chunk') in a PHP block.
// - Shorter syntax for escaping variables \($some_data) in a PHP block.
//
// Usage:
//
// <code>
// stream_wrapper_register('view', 'vpViewStream');
// require_once('view://./templates/some.template.tpl.php');
// </code>
class vpViewStream{
    private $path;
    private $length     = 0;
    private $position   = 0;
    private $stat       = null;
    private $data;

    public function stream_open($path, $mode, $options, $opened_path){
        $path           = str_replace('view://', '', $path);
        $this->data     = file_get_contents($path);
        $this->stat     = stat($path);
        
        if($this->data === false)
            return false;

        if(!@ini_get('short_open_tags')){
            $this->data = str_replace(array('<?=', '<? '), array('<?php echo ', '<?php '), $this->data);
        }
            
        $this->data     = str_replace(array('@$', '@->'), '$this->', $this->data);
        $this->data     = str_replace(array('~(', '\('), array('$this->p_chunk(', '$this->escaped('), $this->data);
        $this->path     = $path;
        $this->length   = strlen($this->data);
        
        return true;
    }
    
    public function stream_seek($offset, $whence){
        switch($whence){
            case SEEK_SET:
                if($offset < $this->length && $offset >= 0){
                    $this->position = $offset;
                    return true;
                }
                break;
            case SEEK_CUR:
                if($offset >= 0){
                    $this->position += $offset;
                    return true;
                }
                break;
            case SEEK_END:
                if($this->length + $offset >= 0){
                    $this->position = $this->length + $offset;
                    return true;
                }
                break;
        }
        
        return false;
    }
    
    public function stream_read($count){
        $str = substr($this->data, $this->position, $count);
        $this->position += strlen($str);
        return $str;
    }
    
    public function stream_tell(){
        return $this->position;
    }
    
    public function stream_stat(){
        return $this->stat;
    }
    
    public function stream_eof(){
        return $this->position > $this->length;
    }
}


De vpViewBase class doet nu iets van require_once('view://path.to.template'); en er word gewoon PHP code uitgevoerd. Dit geeft me ook de mogelijkheid om later héél eenvoudig caching toe te voegen, bijvoorbeeld.
Pagina: 1