hotti: Alternative zu sprintf gesucht

Hi,

soll in der Verwendung so aussehen:

  
$str = fx(  
  "Das ist ein Template wo aus %foo %bar wird mit 20% Zinsen.",  
  array('foo' => 'Verlusten', 'bar => 'Gewinn')  
);  

Gibt es da schon was Fertiges?
Viele Grüße,
Hotti

--
Wenn der Kommentar nicht zum Code passt, kann auch der Code falsch sein.
  1. Hallo,

    alternative Syntax:

    <p>hier steht was <?php echo $myVar["foo"]?> ... <?php echo $myVar["bar"?></p>

    Oder "string" . $var . "string".

    Gruß

    jobo

    1. hi danke,

      aber:

      Oder "string" . $var . "string".

      Genau davon will ich ja wegkommen ;)

      Also ein integrierfähiges kleines Templatesystem, möglichst in einer Klasse gekapselt, so wie ich das hier in Perl habe.

      Wobei: Der Eigenbau ist auch nicht die Frage, die Frage ist, ob es das schon gibt (wie so vieles in PHP).

      Viele Grüße,
      Hotti

      1. Hallo,

        Wobei: Der Eigenbau ist auch nicht die Frage, die Frage ist, ob es das schon gibt (wie so vieles in PHP).

        PHP ist doch eine Templatesprache. Wozu macht sich einer die Mühe, String zu verknuspeln, wenn ich meine PHP-Vars direkt ins HTML reinschreiben kann? Aber guck dir doch mal einschlägige PHP Frameworks an. Da wird sowas für Query-Strings bei Prepared-Statements zB. genutzt (zend-framework und ein haufen anderen, cake, symfony, habdenrestvergessen)

        Gruß

        jobo

        1. Wozu macht sich einer die Mühe, String zu verknuspeln, wenn ich meine PHP-Vars direkt ins HTML reinschreiben kann?

          Zur Internationalisierung beispielsweise. Wenn man statt dessen anfängt, die Teilstrings zu übersetzen, ist man 1. in der Reihenfolge festgelegt und 2. muss man den Kontext der Teilstrings kennen um die richtige Übersetzung zu finden. Wenn dann auch noch der gleiche Teilstring in 2 unterschiedlichen Kontexten vorkommt, wird es unmöglich.

          (Ist jetzt aber auch das einzige Beispiel, das mir dazu einfällt)

  2. Tach.

    $str = fx(
      "Das ist ein Template wo aus %foo %bar wird mit 20% Zinsen.",
      array('foo' => 'Verlusten', 'bar => 'Gewinn')
    );

    
    >   
    > Gibt es da schon was Fertiges?  
      
    Ja, [str_replace](http://uk.php.net/manual/en/function.str-replace.php).  
    
    -- 
    Always remember that you are unique. Just like everybody else.  
    
    
    1. hi, danke!!!

      Ja, str_replace.

      Das sieht sehr gut aus!!! Hier meine Klasse (Wrapper):

        
      # Extended Replace  
      class XR{  
        
      	public function xr($haystack = '', $stash = array()){  
      		$needles = array_keys($stash);  
      		$s = str_replace($needles, $stash, $haystack);  
      		return $s;  
      	}  
        
      }  
        
      $xr = new XR;  
      print $xr->xr("Me and %you and a dog named %boo are %friends\n",  
      	array('%you' => 'Otto', '%boo' => 'Axel', '%friends' => 'dicke Freunde'));  
      # Me and Otto and a dog named Axel are dicke Freunde  
      
      

      Was mir noch nicht so gefällt, ist das Mitschleifen der Prozentzeichen in den Keys (Stash-array). Kriegn wir das noch mit einer Zeile Code hin??

      statt
      '%you' => 'Otto'
      'you' => 'Otto'

      Hotti

      1. hi,

        Was mir noch nicht so gefällt, ist das Mitschleifen der Prozentzeichen in den Keys (Stash-array). Kriegn wir das noch mit einer Zeile Code hin??

        Ok, ich habs:

          
        # Extended Replace  
        class XR{  
        	public function xr($haystack = '', $stash = array()){  
        		$needles = array_map(create_function('$e','return("%$e");'), array_keys($stash));  
        		$s = str_replace($needles, $stash, $haystack);  
        		return $s;  
        	}  
        }  
          
        $xr = new XR;  
        print $xr->xr("Me and %you and a dog named %boo are %friends\n",  
        	array('you' => 'Otto', 'boo' => 'Axel', 'friends' => 'dicke Freunde'));  
        # Me and Otto and a dog named Axel are dicke Freunde  
        
        

        Danke Lobo

        1. [latex]Mae  govannen![/latex]

          Dieser Ansatz schlägt fehl, wenn diverse Bezeichner mit der gleichen Zeichenfolge beginnen.

          print $xr->xr("Me and %you and a %boot named %boo are %friends\n",  
             array('you' => 'Otto', 'boo' => 'Axel', 'friends' => 'dicke Freunde', 'boot' => 'Katze'));  
            
          # Me and Otto and a Axelt named Axel are dicke Freunde
          

          Daher benutze ich in meinem System lieber eine auf preg_replace_callback() basierende Lösung.

          Damit habe ich zusätzlich noch den Vorteil, beispielsweise für nicht im Array gesetzte Bezeichner einen Leerstring oder ähnliches zurückzugeben, wenn der Platzhalter nicht unverändert stehenbleiben soll. Man muß halt entscheiden, ob man bei Mißerfolg den Platzhalter stehenlassen oder durch "nichts" ersetzen will.

          Stur lächeln und winken, Männer!
          Kai

          --
          It all began when I went on a tour, hoping to find some furniture
           Followed a sign saying "Beautiful Chest", led to a lady who showed me her best)
          SelfHTML-Forum-Stylesheet
          1. hi,

            Daher benutze ich in meinem System lieber eine auf preg_replace_callback() basierende Lösung.

            In besonders schweren Fällen benutze ich das auch ;)
            (meine Lösung bisher basiert auf preg-replace-callback)

            Viele Grüße,
            Horst Schreck

          2. Hello Kai,

            [latex]Mae  govannen![/latex]

            Dieser Ansatz schlägt fehl, wenn diverse Bezeichner mit der gleichen Zeichenfolge beginnen.

            print $xr->xr("Me and %you and a %boot named %boo are %friends\n",

            array('you' => 'Otto', 'boo' => 'Axel', 'friends' => 'dicke Freunde', 'boot' => 'Katze'));

            Me and Otto and a Axelt named Axel are dicke Freunde

              
            Das ist aber immer so, wenn keine eindeutigen Start- und Stopsequenzen vereinbart werden, im Prinzip auch bei der Variablenersetzung in PHP (generell) oder eine Stufe tiefer in sprintf(). Da gibt es auch festgelegte Regeln, was einen Bezeichner einleitet und was einen beendet.  
              
            Für ein einfaches Template kann man dafür ja z.B. "<--{" als Startsequenz und "}-->" als Stopsequenz benutzten, oder etwas Vergleichbares. Das machen die Templatesysteme ja fast alle so oder so ähnlich.  
              
              
              
              
              
              
            Liebe Grüße aus dem schönen Oberharz  
              
              
            Tom vom Berg  
            ![](http://selfhtml.bitworks.de/Virencheck.gif)  
              
            
            -- 
             ☻\_  
            /▌  
            / \ Nur selber lernen macht schlau  
            <http://bergpost.annerschbarrich.de>
            
            1. [latex]Mae  govannen![/latex]

              Me and Otto and a Axelt named Axel are dicke Freunde[/code]

              Das ist aber immer so, wenn keine eindeutigen Start- und Stopsequenzen vereinbart werden, [...]

              Ja. Deshalb hatte ich in diesem Fall auch preg_replace_callback vorgeschlagen, da hätte man mit der RegExp relativ einfach festlegen können, wo ein Bezeichner endet (z.B. mit [^a-z0-9_-] oder Ähnlichem).

              Ich weiß schließlich nicht, ob er diese Platzhalter bereits irgendwo massiv einsetzt und „nur“ ein neues Ersetzungs-System baut oder ob alles komplett neu aufgebaut wird. Im zweiten Fall hätte man natürlich die Möglichkeit, sinnvollere Platzhalter zu wählen und sollte dies auch tun.

              Und wie im anderen Beitrag angedeutet .. mir persönlich ist es in der Regel lieber, wenn ungesetzte Platzhalter durch '' ersetzt werden (bzw. beim Debugging durch einen nicht so leicht zu übersehenden Text), das kann ich mit der Callback-Funktion von p_r_c problemlos gleich mit erledigen. Bei str_replace würden diese Platzhalter stehen bleiben, daher mag ich es nicht sonderlich.

              Stur lächeln und winken, Männer!
              Kai

              --
              It all began when I went on a tour, hoping to find some furniture
               Followed a sign saying "Beautiful Chest", led to a lady who showed me her best)
              SelfHTML-Forum-Stylesheet
              1. Hello Kai,

                Und wie im anderen Beitrag angedeutet .. mir persönlich ist es in der Regel lieber, wenn ungesetzte Platzhalter durch '' ersetzt werden (bzw. beim Debugging durch einen nicht so leicht zu übersehenden Text), das kann ich mit der Callback-Funktion von p_r_c problemlos gleich mit erledigen. Bei str_replace würden diese Platzhalter stehen bleiben, daher mag ich es nicht sonderlich.

                Das lese ich anders.

                "If search and replace are arrays, then str_replace()  takes a value from each array and uses them to search and replace on subject. If replace has fewer values than search, then an empty string is used for the rest of replacement values. ..."

                Ich habe es aber jetzt gerade nicht nachvollzogen. Wäre also zu testen, ob das Manual die Wahrheit sagt.

                Liebe Grüße aus dem schönen Oberharz

                Tom vom Berg

                --
                 ☻_
                /▌
                / \ Nur selber lernen macht schlau
                http://bergpost.annerschbarrich.de
  3. nicht ganz. Aber folgendes kommt dem schon relativ nahe:

    $str = vsprintf(
      'Das ist ein Template wo aus %1$s %2$s wird mit 20% Zinsen.',
      array('Verlusten', 'Gewinn')
    );

  4. $str = fx(
      "Das ist ein Template wo aus %foo %bar wird mit 20% Zinsen.",
      array('foo' => 'Verlusten', 'bar => 'Gewinn')
    );

      
    Welchen Vorteil hat das denn gegenüber `sprintf`{:.language-php}?
    
    1. hi,

      $str = fx(
        "Das ist ein Template wo aus %foo %bar wird mit 20% Zinsen.",
        array('foo' => 'Verlusten', 'bar => 'Gewinn')
      );

      
      >   
      > Welchen Vorteil hat das denn gegenüber `sprintf`{:.language-php}?  
        
      sprintf() oder auch vsprintf() wird beim Coden sehr schnell unübersichtlich, genauso wie Stringverkettungen mit Achterbahn-Kontextwechsel. Ich habe z.T. mehr als 10 Werte einzusetzen, da ist ein array() mit namentlich genannten Schlüsseln für die Platzhalter einfach übersichtlicher.  
        
      In Perl fehlt es übrigens auch nicht an 'Versuchen', dieses Manko von sprintf() zu beheben, auf CPAN gibts einige Module zum Thema.  
        
      Nichts geht über einen aufgeräumten Code, egal ob PHP oder Perl:  
        
      ~~~php
        
      $row = $xr->("<tr> <td> %name </td> <td> %vname </td> <td> %plz </td> <td> %ort </td> </tr>",  
        array(  
          'name' => 'Haselhuhn',  
          'vname' => 'Horst',  
          'plz' => '99091',  
          'ort' => 'Hottelstädt',  
          'zbv' => get_zbv($foo, $bar),  
        )  
      );  
      
      

      Hotti

      1. Hello Hotti,

        sprintf() übernimmt alle Werte beim Start und arbeitet dann die Ausgabe ab.

        Bei PHP gibt es str_replace() mit Arrays.
        Das arbeitet viel komplexer, nämlich qusi-rekursiv. Da kommte es nämlich auch noch auf die Reihenfolge der Einträge in den Arrays an.

        Du kannst dadurch mit einem einzigen Durchlauf von str_replace() geschachtelte Templates abarbeiten, indem die am Anfang eingesetzten Ersetzungswerte selber wieder Platzhalter enthalten, die erst später mit der Ersetzung dran sind. Man kann sich so also von außen nach innen in ein HTML-Kontrukt vorarbeiten.

        Was Du genau vor hast, habe ich jetzt nicht versucht nachzuvollziehen, aber ich glaube trotzdem, dass es sich für dich lohnen könnte, mit str_replace() ein wenig zu experimentieren.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. hi Tom,

          Was Du genau vor hast, habe ich jetzt nicht versucht nachzuvollziehen, aber ich glaube trotzdem, dass es sich für dich lohnen könnte, mit str_replace() ein wenig zu experimentieren.

          Na Klar ;)

          Wenn das mit mir alten Knacker weiter so geht, wird aus meiner Perlbase noch eine PHPbase.

          Beispiele zum Nachvollziehen

          D.h.: Interessant wirds mit DB-Abfragen und Alias für Feldnamen. Dann brauchst du nur noch die Referenz in $xr->xr($template, $array); einzusetzen und schwupps hast Du wieder eine Zeile für die Tabelle fertisch ;)

          Hotti (morgen gehts wieder rein ins Vergnügen)

          1. Hotti (morgen gehts wieder rein ins Vergnügen)

            Meinst du das Fußballvergnügen?

            1. Hi lieber Nick,

              Hotti (morgen gehts wieder rein ins Vergnügen)

              Meinst du das Fußballvergnügen?

              Nein, ich meine meinen Job ;)

              Hab derzeit viel zu programmieren und das macht nach >10 Jahren immer noch Spaß. Zu Perl ist mittlerweile PHP hinzugekommen und in Sachen Datenbanken ging es heute wieder richtig rein mit Oracle und MySQL ;)

              Was mir an PHP außerodentlich gut gefällt, ist state, in Perl kam das erst ab 5.010.

              Schönen Feierabend,
              Hotti

              PS: Meine Frau guckt Fußball, ich geh dann mal hoch und mach mich lang...

              --
              Die hohen Spritpreise sind vielen Autofahrern ein Ärgernis (danke Gundula vom ZDF heute, das wusste ich noch nicht).
              1. Hallo lieber Hotti!

                Nein, ich meine meinen Job ;)

                Ach so. :-D

                Hab derzeit viel zu programmieren und das macht nach >10 Jahren immer noch Spaß.

                Das ist doch schön.

                Schönen Feierabend

                Danke, gleichfalls. ;-)

                PS: Meine Frau guckt Fußball, ich geh dann mal hoch und mach mich lang...

                Hehe.

                Viele Grüße
                Nick

                1. hi Nick,

                  PS: Meine Frau guckt Fußball, ich geh dann mal hoch und mach mich lang...

                  Hehe.

                  Für das Spiel gestern habe ich übrigens ein handfestes Alibi: Ich stand im Stau ;)

                  (leider kein Foto, zu langzahm...)

                  SCNR;
                  Horst Blitzlicht

                  1. Für das Spiel gestern habe ich übrigens ein handfestes Alibi: Ich stand im Stau ;)

                    (leider kein Foto, zu langzahm...)

                    SCNR;
                    Horst Blitzlicht

                    :-D

      2. مرحبا

        sprintf() oder auch vsprintf() wird beim Coden sehr schnell unübersichtlich
        Nichts geht über einen aufgeräumten Code, egal ob PHP oder Perl:

        Das geht doch auch mit sprintf();, mMn auch Übersichtlich genug.

          
        $tmpl = '<a data-itemid="%2$s" href="%3$s"%4$s>%1$s</a>';  
        $li .= sprintf('<li%5$s>' . "$tmpl" . '%6$s</li>'  
               /* 1 */ , $f['name']  
               /* 2 */ , $f['itemId']  
               /* 3 */ , $link  
               /* 4 */ , $var  
               /* 5 */ , (strpos(RequestPath, $link) === 0) ? ' class="active_tree"' : ''  
               /* 6 */ , !$tree ? MenuList () : ''  
                      ); 
        

        Ich finde nicht, dass das unübersichtlich ist. Es gibt übrigens noch printf() für die direkte Ausgabe.

        mfg

      3. sprintf() oder auch vsprintf() wird beim Coden sehr schnell unübersichtlich, genauso wie Stringverkettungen mit Achterbahn-Kontextwechsel. Ich habe z.T. mehr als 10 Werte einzusetzen, da ist ein array() mit namentlich genannten Schlüsseln für die Platzhalter einfach übersichtlicher.

        Danke sehr für die Erklärung.

    2. $str = fx(
        "Das ist ein Template wo aus %foo %bar wird mit 20% Zinsen.",
        array('foo' => 'Verlusten', 'bar => 'Gewinn')
      );

      
      >   
      > Welchen Vorteil hat das denn gegenüber `sprintf`{:.language-php}?
      
  5. Gibt es da schon was Fertiges?

    Das ist trivial. Du musst aus diesem Code eigentlich nur die Quelle des templates anpassen:

      
    function GetTemplateReplace($arTemplate) {  
    	if(!isset($arTemplate['TemplateFile'])) {  
    		die ('Fatal: $arTemplate[\'TemplateFile\'] ist nicht gesetzt!'."\n");  
    	}  
    	$template=file_get_contents($arTemplate['TemplateFile']);  
    	$keys=array_keys($arTemplate);  
    	foreach ($keys as $key) {  
    		if ($key != 'TemplateFile') {  
    			$replace[]=$arTemplate[$key];  
    			$search[]='%%%'.$key.'%%%';  
    		}  
    	}  
    	return str_replace($search, $replace, $template);  
    }
    
    1. Tach!

      Liebe Kinder, wenn ihr das zu Hause nachbauen wollt, könnt ihr noch folgende Bemerkungen berücksichtigen.

      function GetTemplateReplace($arTemplate) {
      if(!isset($arTemplate['TemplateFile'])) {
      die ('Fatal: $arTemplate['TemplateFile'] ist nicht gesetzt!'."\n");
      }
      $template=file_get_contents($arTemplate['TemplateFile']);

      Ein Script beim kleinsten Fehler sterben zu lassen ist keine gute Idee. Zudem solltet ihr noch eine wichtigere Prüfung einbauen, die nach der Existenz oder besser der Lesbarkeit der Datei - oder $template auswerten, ob es mit einem false einen Zugriffsfehler signalisiert.

      $keys=array_keys($arTemplate);
      foreach ($keys as $key) {
      if ($key != 'TemplateFile') {
      $replace[]=$arTemplate[$key];
      $search[]='%%%'.$key.'%%%';
      }
      }
      return str_replace($search, $replace, $template);

      Alternative:
      Man muss nicht erst die Array-Keys ermitteln, das hat foreach bereits eingebaut. Ich würde ja den Template-Dateinamen als Extra-Parameter übergeben und nicht mit den Nutzdaten mixen [*]. Ansonsten braucht es hier noch ein unset($arTemplate['TemplateFile']);

      $search = array();  
      foreach ($arTemplate as $key => $content)  
      	$search[] = '%%%'.$key.'%%%';  
      return str_replace($search, $arTemplate, $template);
      

      Das $replace-Array kann ruhig irgendwelche Keys enthalten, die stören nicht. Man braucht sie also nicht extra zu behandeln und kann $arTemplate direkt verwenden.
      Statt foreach kann man auch mit array_map() und einer selbst geschriebenen Funktion (seit PHP 5.3 kann sie auch anonym sein) arbeiten.

      [*] Damit wird auch die erste Prüfung nach dem Vorhandensein des Schlüssels überflüssig.

      dedlfix.

      1. Liebe Kinder, wenn ihr das zu Hause nachbauen wollt, könnt ihr noch folgende Bemerkungen berücksichtigen.

        ...

        Ein Script beim kleinsten Fehler sterben zu lassen ist keine gute Idee. Zudem solltet ihr noch eine wichtigere Prüfung einbauen, die nach der Existenz oder besser der Lesbarkeit der Datei

        Ich kann Dir versichern, das Original prüft mittels einer weiteren Funktion Existenz, Typ, Lesbarkeit und sogar die Rechte an der Datei. Die kann sogar nachsehen, ob die Datei angelegt werden darf und wenn nein, warum.

        Mit Blick auf den Opener (hotti) habe ich es - zugegeben ziemlich notdürftig - aber gleichzeitig ganz gewaltig - zusammengekürzt.

        Ein Script beim kleinsten Fehler sterben zu lassen ist keine gute Idee.

        Dieser Fehler ist fatal. Ohne die Angabe eines Templates macht es keinen Sinn ein Template lesen zu wollen um dann die Ausgaben anzupassen.

        Das $replace-Array kann ruhig irgendwelche Keys enthalten, die stören nicht. Man braucht sie also nicht extra zu behandeln und kann $arTemplate direkt verwenden.

        Danke für diesen heißen Tipp! Damit werde ich mal experimentieren gehen, das würde nämlich Speicher sparen. Im zu Grunde liegenden Beispiel ist es aber so, dass die Elemente von $arTemplate ihrerseits wieder ein Hash sein können (Für Tabellen, Listen etc.) Das habe ich natürlich auch rausgekürzt.

        Darüber hinaus habe ich auch alles was zum Cashing gehört rausgeschmissen. Das Beispiel war aus (m)einem ziemlich vollständigen Templatesystem.

        MfG

        Fred Furunkelstein

        1. Tach!

          Ein Script beim kleinsten Fehler sterben zu lassen ist keine gute Idee.
          Dieser Fehler ist fatal. Ohne die Angabe eines Templates macht es keinen Sinn ein Template lesen zu wollen um dann die Ausgaben anzupassen.

          Aus Sicht dieser Funktion mag das fatal sein, aber ist es das auch in jedem Fall ihrer Anwendung? Schau dir PHPs Funktionen an, die sterben auch nicht so ohne weiteres, sondern teilen das meistens lediglich ihrem Aufrufer mit. Der darf dann entscheiden, was in seinem Zusammenhang angemessen ist.

          dedlfix.

          1. Aus Sicht dieser Funktion mag das fatal sein, aber ist es das auch in jedem Fall ihrer Anwendung?

            Für das Skriptwerk (Anwendung), aus dem ich das entnommen habe, kann ich diese Frage ruhigen Gewissens mit "Ja" beantworten. Für das konkrete Beispiel fällt mir auch kein Umstand ein, wo das nicht fatal wäre. Es sei denn natürlich, man nähme dann ein voreingestelltes Standard-Template. Das käme womöglich sogar als Idee in Betracht. Falls ich das Skriptwerk nochmals erweitere (wozu ich aber einen wirklich guten Grund bräuchte) werde darüber mal nachdenken, die durch Deine Hinweise gewachsene Idee hat eine anheimelnde Wirkung auf mich. Ich muss aber auf womöglich auftretende Seiteneffekte (Nebenwirkungen) prüfen.

            Im Übrigen waren die einzigen Fälle des Auftretens des Problems Typos im Skriptwerk. Benutzer der Webseite(Anwendung) können den Fehler gar nicht erzeugen - sonst hätte ich wirklich was falsch gemacht.

            Grüße

            Fred Furunkelstein