{
  "version": "2.0",
  "xmlns": {
    "atom": "http://www.w3.org/2005/Atom",
    "content": "http://purl.org/rss/1.0/modules/content/",
    "georss": "http://www.georss.org/georss",
    "gml": "http://www.opengis.net/gml"
  },
  "channel": {
    "title": "fboës - Der Blog | Artikel mit dem Tag \"PHP\"",
    "link": "https://journal.3960.org/",
    "description": "Programmierung, Luft- & Raumfahrt, Kurioses: Der Blog von und mit Frank Boës.",
    "language": "de-DE",
    "copyright": "© 2008-2023 Creative Commons BY",
    "atom_link": {
      "href": "https://journal.3960.org/tagged/php/rss.json",
      "rel": "self",
      "type": "application/rss+json"
    },
    "lastBuildDate": "Tue, 24 Mar 2026 20:00:16 +0100",
    "atom_updated": "2026-03-24T20:00:16+01:00",
    "generator": "blogophon",
    "image": {
      "url": "https://cdn.3960.org/images/tile-128x128.png",
      "title": "fboës - Der Blog",
      "link": "https://journal.3960.org/"
    },
    "items": [
      {
        "title": "World's smallest PHP template engine",
        "description": "<p>A fundamental principle of building (web) applications is to have a strict separation between logic and output – at least if your goal as a programmer is to stay sane. For outputting stuff most often there is a simple (template) language to prevent mixing too much logic into your templates.</p>\n<p>For PHP template engines like <a href=\"https://twig.symfony.com/\">Twig</a> or <a href=\"https://www.smarty.net/\">Smarty</a> solve the outputting part. But do we really need a template engine? Is it possible that PHP (without any additional libraries) is quite sufficient to do templating?</p>",
        "content_encoded": "<p>A fundamental principle of building (web) applications is to have a strict separation between logic and output – at least if your goal as a programmer is to stay sane. For outputting stuff most often there is a simple (template) language to prevent mixing too much logic into your templates.</p>\n<p>For PHP template engines like <a href=\"https://twig.symfony.com/\">Twig</a> or <a href=\"https://www.smarty.net/\">Smarty</a> solve the outputting part. But do we really need a template engine? Is it possible that PHP (without any additional libraries) is quite sufficient to do templating?</p>\n<!-- more -->\n<p id=\"more\">The main idea of template engines is:</p>\n<ul>\n<li>Template engines remove logic on purpose – to concentrate complexity in your programming in a different spot, and to remove additional points of error.</li>\n<li>The remaining control structures like <code>if</code>, <code>while</code> or <code>for</code> are more simple to use in a template engine.</li>\n<li>Template engines take care of proper encoding / quoting whenever you are outputting a variable.</li>\n<li>Template engines support re-use of template parts (called snippets or partials) in different context.</li>\n</ul>\n<p>A perfect example for this philosophy is the template engine <a href=\"http://mustache.github.io/\">Mustache</a>: It only contains a minimum of required methods in order to reduce complexity.</p>\n<p>But do we really need the overhead of a proper template engine? Or is it possible to use plain PHP for our templating needs, without adding dependencies like template engines to our project?</p>\n<p>Actually PHP has good capabilities to do templating – its <a href=\"https://www.php.net/manual/en/intro-whatis.php\">founding idea revolves around templating</a>, as a matter of fact.</p>\n<h2 id=\"file-extensions\">File Extensions</h2>\n<p>It is common practice to put all your templates into a separate folder, and append a common file extension to all template files. For example <a href=\"https://symfony.com/doc/current/templates.html#creating-templates\">Symfony with Twig</a> puts all templates into a directory called <code>/templates</code> and use the file extension <code>.twig</code>.</p>\n<p>Actually there is nothing stopping us from using the same <code>/templates</code> directory for our templates. As file extension we are using (like Twig and to keep our mental health):</p>\n<div class=\"table-wrapper\"><table>\n<thead>\n<tr>\n<th>Template output</th>\n<th>File extension</th>\n<th>(MIME-type)</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>HTML file</td>\n<td class=\"tag-code\"><code>*.html.php</code></td>\n<td class=\"tag-code\"><code>text/html</code></td>\n</tr>\n<tr>\n<td>XML file</td>\n<td class=\"tag-code\"><code>*.xml.php</code></td>\n<td><code>text/xml</code> or <code>application/xml</code></td>\n</tr>\n<tr>\n<td>JSON file</td>\n<td class=\"tag-code\"><code>*.json.php</code></td>\n<td class=\"tag-code\"><code>application/json</code></td>\n</tr>\n<tr>\n<td>Text file</td>\n<td class=\"tag-code\"><code>*.txt.php</code></td>\n<td class=\"tag-code\"><code>text/plain</code></td>\n</tr>\n<tr>\n<td>CSV file</td>\n<td class=\"tag-code\"><code>*.csv.php</code></td>\n<td class=\"tag-code\"><code>text/csv</code></td>\n</tr>\n</tbody></table></div>\n<p>This list is easily extended be even more MIME-types and corresponding file extensions. Important for us is that all files end with <code>.php</code>, so our code editor knows how to handle these files. This also secures the files if by mistake these files get called directly by a web server; files ending with <code>.php</code> will be executed as PHP, and their PHP secrets will be compiled and not shown to a world-wide audience.</p>\n<p>It is good custom to name your template after the controller calling it. Accordingly a HTML template for an index-controller will be <code>index.html.php</code>, for an user-controller <code>user.html.php</code>. The XML template for the index-controller becomes <code>index.xml.php</code>.</p>\n<p>Templates not attached to a controller (called snippets or partials) will be prefixed with a <code>_</code>, e.g. <code>_meta.html.php</code>. The purpose of these files will be explained later on.</p>\n<h2 id=\"variable-output\">Variable Output</h2>\n<p>We will take advantage of the fact that PHP returns every line of code not enclosed in <a href=\"https://www.php.net/manual/en/language.basic-syntax.phptags.php\">PHP tags</a> (<code>&lt;?php ?&gt;</code>) unaltered back to the browser. That allows us to concentrate on building our beautiful template output:</p>\n<pre><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;<i>html</i> <var>xmlns</var>=&quot;<kbd>http://www.w3.org/1999/xhtml</kbd>&quot; <var>lang</var>=&quot;<kbd>en</kbd>&quot;&gt;\n&lt;<i>head</i>&gt;\n&lt;/<i>head</i>&gt;\n&lt;<i>body</i>&gt;\n  <u>&lt;!-- I am valid PHP --&gt;</u>\n&lt;/<i>body</i>&gt;\n</code></pre>\n<p>The most important task of templates is to output variables prepared by the controller. Like in every template language we open up a instruction block and execute a function for outputting a variable.</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?php echo($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>PHP kindly allows for shortening the output instruction:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Still a bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= $title ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>But actually the above examples are very bad ideas, because both ignore quoting / escaping – a core function of any good template engine, because only this secures variable output against malicious intentions. Look at the above examples and imagine some HTML tags (or characters like a single <code>&lt;</code>) inside <code>$title</code> – as you can see these will be embedded into the HTML output. In most cases this is undesirable, possibly breaking your site or (even worse) allowing attackers to make your site send harmful HTML to your visitor&#39;s browsers. Want to see an example?</p>\n<pre><code class=\"language-html\"><u>&lt;!-- See, a bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= $_GET[&#39;search&#39;] ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>But PHP has you covered: <a href=\"https://www.php.net/htmlspecialchars\"><code>htmlspecialchars</code></a> converts any string to safe HTML (or XML, for that matter). Characters like <code>&lt;</code>, <code>&gt;</code> or <code>&quot;</code> will be safely encoded:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Better idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= htmlspecialchars($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Because this main method of outputting variables will become tedious to write, we are introducing an <em>alias</em> for <code>htmlspecialchars</code> – the beginning of the world&#39;s smallest PHP template engine:</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * Alias for `htmlspecialchars`\n */</u>\n<b>function</b> html(<var>$s</var>): <b>string</b>\n{\n  <i>return</i> htmlspecialchars(<var>$s</var>);\n}\n</code></pre>\n<p>This function shortens our work to convert variables and will be our new main function to send variable content back to the browser:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Best idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<h3 id=\"more-quoting\">More Quoting</h3>\n<p>PHP offers even more function for quoting / escaping. There is a function for encoding query parameters used in URLs, quoting characters like <code>#</code> or <code>&amp;</code>: <a href=\"https://www.php.net/urlencode\"><code>urlencode</code></a>. These can be easily combined with our <code>html</code> function:</p>\n<pre><code class=\"language-html\">&lt;<i>a</i> href=&quot;https://www.example.com?id=<b>&lt;?= html(urlencode($id)) ?&gt;</b>&quot;&gt;Test&lt;/<i>a</i>&gt;\n</code></pre>\n<p>Sometimes we also have to convert our PHP variables to JavaScript variables. Not explicitly build for that purpose but highly recommendable is a function called <a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a>. It actually converts the given variable to JSON – which is also valid JavaScript. Even better the function also converts PHP arrays and objects to JavaScript arrays and objects:</p>\n<pre><code class=\"language-html\">&lt;<i>script</i>&gt;\nvar data = <b>&lt;?= json_encode($data) ?&gt;</b>;\n&lt;/<i>script</i>&gt;\n</code></pre>\n<h2 id=\"conditions\">Conditions</h2>\n<p>The most important control structure in templates are conditions, with your best friend <code>if</code> to be mentioned first. But „pure“ PHP with its curly brackets used in conjunction with <code>if</code> tends to make matters confusing in your template, making it hard to see where your conditional block ends:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($title)) { ?&gt;</b>\n  &lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n<b>&lt;?php } ?&gt;</b>\n</code></pre>\n<p>Do not despair, <a href=\"https://www.php.net/manual/en/control-structures.alternative-syntax.php\">PHP has an alternative syntax for controls structures</a> tailored for templating tasks. Instead of starting a block via <code>{</code> a simple  <code>:</code> is used. And instead of ending a block via <code>}</code> something like <code>end...;</code> will be used:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($title)): ?&gt;</b>\n  &lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<p>Your <code>if</code>-conditions can use any operators and functions known to PHP. One of your most important conditions in templating will be a check if a given variable <em>has</em> content to output. This test is simply executed by using <code>!empty</code> or <code>isset</code>.</p>\n<p>Of course there is also <code>else</code> and <code>elseif</code> available:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($results)): ?&gt;</b>\n  &lt;<i>h4</i>&gt;We have found <b>&lt;?= html(count($results)) ?&gt;</b> results.&lt;/<i>h4</i>&gt;\n<b>&lt;?php else: ?&gt;</b>\n  &lt;<i>h4</i>&gt;Sorry, we have found no results.&lt;/<i>h4</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<h2 id=\"loops-and-arrays\">Loops and Arrays</h2>\n<p>Then there are lists and tables in your templates. With PHP loops your are able to iterate over an PHP array to output lists and tables. Lucky for you <a href=\"https://www.php.net/manual/en/control-structures.alternative-syntax.php\">there is also an alternate syntax for looping control structures</a>:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($list)): ?&gt;</b>\n  &lt;<i>ul</i>&gt;\n    <b>&lt;?php foreach($list as $index =&gt; $item): ?&gt;</b>\n      &lt;<i>li</i>&gt;<b>&lt;?= html($item) ?&gt;</b>&lt;/<i>li</i>&gt;\n    <b>&lt;?php endforeach ?&gt;</b>\n  &lt;/<i>ul</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<p>This syntax does a proper job for <code>for</code>, <code>foreach</code> or <code>while</code>:</p>\n<pre><code class=\"language-html\">&lt;<i>h4</i>&gt;Lottery numbers&lt;/<i>h4</i>&gt;\n&lt;<i>ol</i>&gt;\n  <b>&lt;?php for ($i = 1; $i &lt;= 49; $i++): ?&gt;</b>\n    &lt;<i>li</i>&gt;\n      &lt;<i>input</i> <var>type</var>=&quot;<kbd>checkbox</kbd>&quot; name=&quot;lottery_<b>&lt;?= html($i) ?&gt;</b>&quot; id=&quot;lottery_<b>&lt;?= html($i) ?&gt;</b>&quot; value=&quot;1&quot; /&gt;\n      &lt;<i>label</i> for=&quot;lottery_<b>&lt;?= html($i) ?&gt;</b>&quot;&gt;<b>&lt;?= html($i) ?&gt;</b>&lt;/<i>label</i>&gt;\n    &lt;/<i>li</i>&gt;\n  <b>&lt;?php endfor ?&gt;</b>\n&lt;/<i>ol</i>&gt;\n</code></pre>\n<h2 id=\"snippets--partials\">Snippets / Partials</h2>\n<p>Partial templates present in multiple controllers (like a header, footer or navigation) are called… partials (doh!) or snippets. These partials are prefixed with a single <code>_</code>, for example <code>_header.xml.php</code>.</p>\n<p>These partials or snippets can be embedded into other templates by PHP&#39;s very own <code>include</code> instruction:</p>\n<pre><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;<i>html</i> <var>xmlns</var>=&quot;<kbd>http://www.w3.org/1999/xhtml</kbd>&quot; <var>lang</var>=&quot;<kbd>en</kbd>&quot;&gt;\n&lt;<i>head</i>&gt;\n  <b>&lt;?php include(&#39;_meta.html.php&#39;) ?&gt;</b>\n&lt;/<i>head</i>&gt;\n&lt;<i>body</i>&gt;\n  <b>&lt;?php include(&#39;_header.html.php&#39;) ?&gt;</b>\n  ...\n  <b>&lt;?php include(&#39;_footer_.html.php&#39;) ?&gt;</b>\n&lt;/<i>body</i>&gt;\n</code></pre>\n<h2 id=\"translations\">Translations</h2>\n<p>You never would have guessed, but PHP also handles translations! The <a href=\"https://www.php.net/manual/en/book.gettext.php\">Gettext library</a> is included in PHP and allows to translate strings, which Gettext will look up in a dictionary you are able to build. If a translation is found, Gettext will return it to PHP.</p>\n<p>Most important for translations is to tell PHP the language / locale you are using. This is done via <a href=\"https://www.php.net/setlocale\"><code>setlocale</code></a>:</p>\n<pre><code class=\"language-php\">setlocale(LC_MESSAGES, <kbd>'de_DE'</kbd>); <u>// German in Germany</u>\nsetlocale(LC_MESSAGES, <kbd>'en_GB'</kbd>); <u>// English in Great Britain</u>\n</code></pre>\n<p>This also switches to the correct format for numbers and dates in the given language, as well as using the correct date terms for months and weekdays.</p>\n<p>The strings you want translated are passed to the <a href=\"https://www.php.net/_\"><code>gettext</code> / <code>_</code> function</a>:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- translate to &#39;Guten Morgen!&#39; --&gt;</u>\n&lt;<i>h4</i>&gt;<b>&lt;?= html(_(&#39;Good morning!&#39;)) ?&gt;</b>&lt;/<i>h4</i>&gt;\n</code></pre>\n<p>The required dictionaries to translate your strings can be created by yourself. The required <a href=\"http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files\">PO- and MO-files</a> can be created using programmes like <a href=\"https://poedit.net/\">Poedit</a>. <i>Poedit</i> also offers to search your whole project for translatable strings to automatically add them to your dictionaries.</p>\n<p>If your happen to add variables to your translatable strings things can get a little hairy. This somewhat complex construction allows for a translatable string to also work with variables:</p>\n<pre><code class=\"language-html\">&lt;<i>div</i>&gt;<b>&lt;?= html(vsprintf(_(&#39;There are %d results&#39;), [$count])) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>So here is our next alias to be added to our simple template engine: A shortcut for outputting translatable strings mixed with variables.</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * Alias for `vsprintf`, but with HTML escaping and translation\n */</u>\n<b>function</b> _html(<b>string</b> <var>$format</var>, <b>array</b> <var>$args</var> = []): <b>string</b>\n{\n  <i>return</i> htmlspecialchars(<var>$args</var>\n    ? vsprintf(_(<var>$format</var>), <var>$args</var>)\n    : _(<var>$format</var>)\n  );\n}\n</code></pre>\n<p>…which makes writing translatable strings mixed with variables somewhat less tedious:</p>\n<pre><code class=\"language-html\">&lt;<i>h4</i>&gt;<b>&lt;?= _html(&#39;Good morning!&#39;) ?&gt;</b>&lt;/<i>h4</i>&gt;\n&lt;<i>div</i>&gt;<b>&lt;?= _html(&#39;There are %d results&#39;, [$count]) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<h2 id=\"variable-dumping\">Variable Dumping</h2>\n<p>It is very helpful for developers as well as template builders alike to have a look into complex variable constructs for debugging purposes – think of big objects or arrays to traverse. PHP offers <a href=\"https://www.php.net/print_r\"><code>print_r</code></a> and <a href=\"https://www.php.net/var_dump\"><code>var_dump</code></a> / <a href=\"https://www.php.net/var_export\"><code>var_export</code></a> for this purpose. These functions are helpful, but not in HTML as they do not produce any line breaks, dumping all your structured data into a single line. Again our little template engine comes to the rescue with a small shortcut:</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * HTML dumper für PHP variables\n */</u>\n<b>function</b> debug(<var>$mixed</var>, <b>bool</b> <var>$extended</var> = <samp>false</samp>): <b>void</b>\n{\n  <i>echo</i>(<kbd>'&lt;pre class=&quot;debug&quot; style=&quot;margin: 1em 0; border: 1px solid red; background: #fee; color: #000; padding: 1em;&quot;&gt;'</kbd>);\n  <i>echo</i>(htmlspecialchars(<var>$extended</var>\n    ? var_export(<var>$mixed</var>, <samp>true</samp>)\n    : print_r(<var>$mixed</var>, <samp>true</samp>)\n  ));\n  <i>echo</i>(<kbd>'&lt;/pre&gt;'</kbd>);\n}\n</code></pre>\n<p>This allows for complex variables to be dumped in a readable manner:</p>\n<pre><code class=\"language-html\"><b>&lt;?php debug($data) ?&gt;</b>\n</code></pre>\n<p>While your at it, you might consider <a href=\"https://journal.3960.org/posts/einfachste-php-objekt-debugger-fuer-browser/\">using <code>console.log</code> for debugging output</a> in the console of your browser.</p>\n<h2 id=\"outputting-different-content-types\">Outputting different Content-Types</h2>\n<p>PHP was build to output HTML. This works by PHP telling the browser that the MIME-type of the document it is sending is <code>text/html</code>. But actually PHP is perfectly capable to send any other MIME-type header. The function <a href=\"https://www.php.net/header\"><code>header</code></a> allows PHP to send custom headers to your visitor&#39;s browsers, replacing default headers while your at it. </p>\n<p>The header used to set the MIME-type is <code>Content-Type</code>. So it is perfectly possible for PHP to send <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\">any conceivable MIME-type</a>.</p>\n<h3 id=\"xml\">XML</h3>\n<p>For XML we choose the file extension <code>.xml.php</code>, for example in <code>sitemap.xml.php</code>. This template does not differ very much from a standard HTML template, and uses the same quoting / encoding routines. We just have to send a different <code>Content-Type</code> header and we are serving proper XML:</p>\n<pre><code class=\"language-xml\"><b>&lt;?php header(&#39;Content-Type: text/xml&#39;); ?&gt;</b>\n&lt;<i>urlset</i>&gt;\n  <b>&lt;?php foreach($urls as $url): ?&gt;</b>\n    &lt;<i>url</i>&gt;\n      &lt;<i>loc</i>&gt;<b>&lt;?= html($url) ?&gt;</b>&lt;/<i>loc</i>&gt;\n    &lt;/<i>url</i>&gt;\n  <b>&lt;?php endforeach ?&gt;</b>\n&lt;/<i>urlset</i>&gt;\n</code></pre>\n<h3 id=\"json\">JSON</h3>\n<p>Outputting JSON is also quite simple with PHP. For this MIME-type we are using the file extension <code>.json.php</code> (like in <code>feed.json.php</code>) and need to set the correct <code>Content-Type</code>. To properly use quoting / escaping the inbuilt PHP function <a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a> is very helpful, as it converts even complex PHP variables into structured JSON output:</p>\n<pre><code class=\"language-php\">&lt;?php\n  header(<kbd>'Content-Type: application/json'</kbd>);\n  <i>echo</i>(json_encode(<var>$data</var>));\n</code></pre>\n<p>If you like your JSON&#39;s source to look nice, there is also a parameter called <code>JSON_PRETTY_PRINT</code> for <code>json_encode</code>:</p>\n<pre><code class=\"language-php\">&lt;?php\n  header(<kbd>'Content-Type: application/json'</kbd>);\n  <i>echo</i>(json_encode(<var>$data</var>, JSON_PRETTY_PRINT));\n</code></pre>\n<p>Actually PHP&#39;s <code>json_encode</code> precisely converts PHP variable types to the matching JSON types. A PHP integer will be a JSON integer, a PHP string will be a JSON string. So note the difference between <code>1</code> and <code>„1“</code>.</p>\n<h3 id=\"get-the-right-template\">Get the right template</h3>\n<p>The examples above for fetching templates and setting the correct MIME-type can be solved with a general <code>switch</code> block:</p>\n<pre><code class=\"language-php\"><u>// e.g. $template = 'index';</u>\n<u>// e.g. $contentType = 'html';</u>\n\n<i>switch</i> (<var>$contentType</var>) {\n  <i>case</i> <kbd>'xml'</kbd>:\n    header(<kbd>'Content-Type: text/xml'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'json'</kbd>:\n    header(<kbd>'Content-Type: application/json'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'txt'</kbd>:\n    header(<kbd>'Content-Type: text/plain'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'csv'</kbd>:\n    header(<kbd>'Content-Type: text/csv'</kbd>);\n    <i>break</i>;\n  default:\n    <var>$contentType</var> = <kbd>'html'</kbd>;\n    <i>break</i>;\n}\n\n<var>$templateFilename</var> = __DIR__ . <kbd>'/templates/'</kbd> . <var>$template</var> . <kbd>'.'</kbd> . <var>$contentType</var> . <kbd>'.php'</kbd>;\n<i>require</i>(<var>$templateFilename</var>);\n</code></pre>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>EVery PHP project needs to have a clean separation between logic and output – but not always you will need a template engine like <a href=\"https://twig.symfony.com/\">Twig</a> or <a href=\"https://www.smarty.net/\">Smarty</a> for this separation. Common off-the-shelf PHP (with few small helpers) can be a simple, fuss-free alternative for templating.</p>\n<p>So here it is, our small manual of <a href=\"https://www.php.net/manual/en/ref.strings.php\">the most important PHP functions for templating</a>:</p>\n<div class=\"table-wrapper\"><table>\n<thead>\n<tr>\n<th>Function</th>\n<th>Alias</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><a href=\"https://www.php.net/htmlspecialchars\"><code>htmlspecialchars</code></a></td>\n<td class=\"tag-code\"><code>html</code></td>\n<td>HTML escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/urlencode\"><code>urlencode</code></a></td>\n<td>-</td>\n<td>URL escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a></td>\n<td>-</td>\n<td>JSON/JavaScript escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/setlocale\"><code>setlocale</code></a></td>\n<td>-</td>\n<td>Set language / locale for translation</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/_\"><code>gettext</code> / <code>_</code></a></td>\n<td class=\"tag-code\"><code>_html</code></td>\n<td>Outputting translation</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/strftime\"><code>strftime</code></a></td>\n<td>-</td>\n<td>Output localized date format</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/localeconv\"><code>localeconv</code></a></td>\n<td>-</td>\n<td>Get number format for current locale</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/printf\"><code>printf</code></a></td>\n<td class=\"tag-code\"><code>_html</code></td>\n<td>Output formatted variables in string</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/nl2br\"><code>nl2br</code></a></td>\n<td>-</td>\n<td>Convert line breaks to <code>&lt;br /&gt;</code></td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/implode\"><code>implode</code></a></td>\n<td>-</td>\n<td>Joins array members to a single string</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/var_dump\"><code>var_dump</code></a></td>\n<td class=\"tag-code\"><code>debug</code></td>\n<td>Dump PHP variables</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/header\"><code>header</code></a></td>\n<td>-</td>\n<td>Change <code>Content-Type</code> in browser</td>\n</tr>\n</tbody></table></div>\n<p>The only thing missing from PHP is template inheritance, like in <a href=\"https://twig.symfony.com/doc/3.x/templates.html#template-inheritance\">Twig&#39;s template inheritance</a> or <a href=\"https://www.smarty.net/docs/en/advanced.features.template.inheritance.tpl\">Smarty&#39;s template inheritance</a>. Also have a look at <a href=\"https://css-tricks.com/comparing-html-preprocessor-features/\"><i>CSS-Trick&#39;s</i> comparison of template engines</a>.</p>\n<hr />\n<p>Es gibt auch eine <a href=\"https://journal.3960.org/posts/2020-08-20-kleinste-php-templating-engine-welt/\" rel=\"alternate\">deutsche Version von diesem Artikel, „Die kleinste PHP-Templating-Engine der Welt“</a>.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=World's%20smallest%20PHP%20template%20engine&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2021-12-17-world-smallest-php-templating-engine%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/2021-12-17-world-smallest-php-templating-engine/",
        "pubDate": "Fri, 17 Dec 2021 18:48:53 +0100",
        "atom_published": "2021-12-17T18:48:53+01:00",
        "atom_updated": "2024-02-02T17:26:09+01:00",
        "guid": "user/posts/2021-12-17-world-smallest-php-templating-engine/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Webdevelop"
        ]
      },
      {
        "title": "Die kleinste PHP-Templating-Engine der Welt",
        "description": "<p>Die Trennung von Logik und Ausgabe in einer (Web-)Anwendung ist eines der fundamentalen Prinzipien, um als Programmierer seine geistige Gesundheit zu behalten. Für die Ausgabe wird dabei eine zumeist einfache (Template-)Sprache verwendet, die verhindern soll, dass zu viel Logik in die Ausgabe wandert.</p>\n<p>In PHP wird dies in der Regel von Template-Engines wie <a href=\"https://twig.symfony.com/\">Twig</a> oder <a href=\"https://www.smarty.net/\">Smarty</a> gelöst. Aber warum benötigen wir eigentlich eine Template-Engine? Könnte es sein, dass PHP ohne weitere Zusätze auch eine ganz passable Lösung für Templating parat hält?</p>",
        "content_encoded": "<p>Die Trennung von Logik und Ausgabe in einer (Web-)Anwendung ist eines der fundamentalen Prinzipien, um als Programmierer seine geistige Gesundheit zu behalten. Für die Ausgabe wird dabei eine zumeist einfache (Template-)Sprache verwendet, die verhindern soll, dass zu viel Logik in die Ausgabe wandert.</p>\n<p>In PHP wird dies in der Regel von Template-Engines wie <a href=\"https://twig.symfony.com/\">Twig</a> oder <a href=\"https://www.smarty.net/\">Smarty</a> gelöst. Aber warum benötigen wir eigentlich eine Template-Engine? Könnte es sein, dass PHP ohne weitere Zusätze auch eine ganz passable Lösung für Templating parat hält?</p>\n<!-- more -->\n<p id=\"more\">Die Hauptidee hinter den meisten Template-Engines ist:</p>\n<ul>\n<li>Template-Engines bieten mit Vorsatz wenig Logik, um die Komplexität einer Programmierung an einer anderen Stelle zu bündeln, und Fehlerquellen zu vermeiden.</li>\n<li>Dafür bieten Template-Engines einfach zu bedienende Kontrollstrukturen wie <code>if</code>, <code>while</code> und <code>for</code>.</li>\n<li>Template-Engines kümmern sich um die korrekte Ausgabe von Variablen (Quoting / Escaping).</li>\n<li>Template-Engines erlauben die mehrfache Verwendung von Template-Teilen (Snippets / Partials) in verschiedenen Kontexten.</li>\n</ul>\n<p>Ein ausgezeichnetes Beispiel für die Philosophie von Template-Engines ist <a href=\"http://mustache.github.io/\">Mustache</a>, das ein absolutes Minimum von Methoden anbietet, um Komplexität zu verhindern.</p>\n<p>Aber brauchen wir in PHP den Overhead einer Template-Engine wirklich? Oder können wir nicht mit ein bisschen Disziplin direkt mit PHP alle unsere Template-Bedürfnisse stillen – ohne uns in Abhängigkeit zu einer weiteren Software-Bibliothek zu begeben?</p>\n<p>Tatsächlich bietet PHP (auch aufgrund seiner <a href=\"https://www.php.net/manual/de/intro-whatis.php\">Genese</a>) sehr gute Möglichkeiten, Templating damit zu betreiben.</p>\n<h2 id=\"dateiendungen\">Dateiendungen</h2>\n<p>Template-Engines legen ihre Templates in der Regel in einen eigenen Ordner mit einer eigenen Dateiendung ab. In der Kombination <a href=\"https://symfony.com/doc/current/templates.html#creating-templates\">Symfony mit Twig</a> liegen Templates im Projekt in einem Ordner <code>/templates</code> und haben die Dateiendung <code>.twig</code>.</p>\n<p>Tatsächlich hindert uns nichts daran, unsere PHP-Templates ebenfalls in einem <code>/templates</code>-Verzeichnis abzulegen. Als Datei-Endung verwenden wir dabei (analog zu Twig und zur Erhaltung der geistigen Gesundheit):</p>\n<div class=\"table-wrapper\"><table>\n<thead>\n<tr>\n<th>Template-Ausgabe</th>\n<th>Datei-Endung</th>\n<th>(MIME-Type)</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>HTML-Dateien</td>\n<td class=\"tag-code\"><code>*.html.php</code></td>\n<td class=\"tag-code\"><code>text/html</code></td>\n</tr>\n<tr>\n<td>XML-Dateien</td>\n<td class=\"tag-code\"><code>*.xml.php</code></td>\n<td><code>text/xml</code> oder <code>application/xml</code></td>\n</tr>\n<tr>\n<td>JSON-Dateien</td>\n<td class=\"tag-code\"><code>*.json.php</code></td>\n<td class=\"tag-code\"><code>application/json</code></td>\n</tr>\n<tr>\n<td>Text-Dateien</td>\n<td class=\"tag-code\"><code>*.txt.php</code></td>\n<td class=\"tag-code\"><code>text/plain</code></td>\n</tr>\n<tr>\n<td>CSV-Dateien</td>\n<td class=\"tag-code\"><code>*.csv.php</code></td>\n<td class=\"tag-code\"><code>text/csv</code></td>\n</tr>\n</tbody></table></div>\n<p>Diese Liste lässt sich natürlich beliebig um neue Dateitypen erweitern. Wichtig ist nur, dass die Dateien auf PHP enden, damit sie einerseits von einem Code-Editor als PHP-Dateien erkannt werden, andererseits beim irrtümlichen Aufruf über einen Webserver auch als PHP ausgeführt werden, und nicht irgendwelche PHP-Geheimnisse verraten.</p>\n<p>Nach alter Sitte benennen wir das Template nach dem Controller. So wird das HTML-Template für den Index-Controller <code>index.html.php</code>, für den User-Controller <code>user.html.php</code> benannt. Das XML-Template für den Index-Controller ist dann entsprechend <code>index.xml.php</code>.</p>\n<p>Templates, die keinen Controller zugeordnet werden können (sogenannte Snippets oder Partials) beginnen mit einem <code>_</code>, also zum Beispiel <code>_meta.html.php</code>. Was es mit diesen Dateien auf sich hat klären wir weiter unten.</p>\n<h2 id=\"variablen-ausgabe\">Variablen-Ausgabe</h2>\n<p>Wir nutzen die Fähigkeit von PHP, dass jede Anweisung, die nicht in <a href=\"https://www.php.net/manual/de/language.basic-syntax.phptags.php\">PHP-Tags</a> (<code>&lt;?php ?&gt;</code>) steht, nicht als PHP verstanden wird, sondern direkt an den Browser zurückgegeben wird. Damit können wir innerhalb unserer Template-Dateien uns tatsächlich auf die Ausgabe konzentrieren:</p>\n<pre><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;<i>html</i> <var>xmlns</var>=&quot;<kbd>http://www.w3.org/1999/xhtml</kbd>&quot; <var>lang</var>=&quot;<kbd>en</kbd>&quot;&gt;\n&lt;<i>head</i>&gt;\n&lt;/<i>head</i>&gt;\n&lt;<i>body</i>&gt;\n  <u>&lt;!-- I am valid PHP --&gt;</u>\n&lt;/<i>body</i>&gt;\n</code></pre>\n<p>Die wichtigste Funktion eines Templates ist die Ausgabe von Variablen, die im Controller vorbereitet wurden. Dazu öffnen wir (wie in jeder Template-Sprache) einen Kommandoblock, und führen darin das Kommando zum Ausgeben von Variablen aus.</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?php echo($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Netterweise gibt es eine PHP-Tag, das diese Ausgabe nochmals verkürzt:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Still a bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= $title ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Tatsächlich sind aber beide Wege eine ganz schlechte Idee, weil wir hier sträflich das Quoting / Escaping vernachlässigt haben – eine Kernfunktionalität einer jeden Template-Engine und ein Garant für die Sicherheit unserer Web-Applikation. Im obigen Beispiel wäre es möglich, dass in <code>$title</code> HTML steckt, was direkt wieder auf der Seite ausgegeben werden kann. In den wenigsten Fällen ist dies erwünscht und setzt voraus, dass man dem Variablen-Inhalt jederzeit vertrauen kann. Beispiel gefällig?</p>\n<pre><code class=\"language-html\"><u>&lt;!-- See, a bad idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= $_GET[&#39;search&#39;] ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Praktischerweise liefert PHP die Funktion <a href=\"https://www.php.net/htmlspecialchars\"><code>htmlspecialchars</code></a> mit, die einen String für die Ausgabe in sicherem HTML umwandelt. So werden Zeichen wie <code>&lt;</code>, <code>&gt;</code> und <code>&quot;</code> sicher umgewandelt:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Better idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= htmlspecialchars($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Da diese Art der Ausgabe unsere Hauptmethode sein soll, die Schreibweise aber etwas sperrig ist, bauen wir uns einen <em>Alias</em> für <code>htmlspecialchars</code>. Der Beginn der kleinsten PHP-Template-Engine der Welt:</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * Alias for `htmlspecialchars`\n */</u>\n<b>function</b> html(<var>$s</var>): <b>string</b>\n{\n  <i>return</i> htmlspecialchars(<var>$s</var>);\n}\n</code></pre>\n<p>Diese Funktion kürzt uns die Schreibarbeit ab, um eine Variable sicher auszugeben, und ist ab sofort unsere Hauptfunktion zur Ausgabe von Variablen:</p>\n<pre><code class=\"language-html\"><u>&lt;!-- Best idea --&gt;</u>\n&lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<h3 id=\"weiteres-quoting\">Weiteres Quoting</h3>\n<p>PHP bietet aber noch mehr Funktion für Quoting / Escaping. Unter anderem müssen wir bei der Ausgabe von Query-Parametern in URLs Zeichen wie <code>#</code> und <code>&amp;</code> umwandeln. Dafür hat PHP die Funktion <a href=\"https://www.php.net/urlencode\"><code>urlencode</code></a> parat  – in Kombination mit unserer <code>html</code>-Funktion unschlagbar:</p>\n<pre><code class=\"language-html\">&lt;<i>a</i> href=&quot;https://www.example.com?id=<b>&lt;?= html(urlencode($id)) ?&gt;</b>&quot;&gt;Test&lt;/<i>a</i>&gt;\n</code></pre>\n<p>Dann und wann müssen wir auch PHP-Variablen in JavaScript-Variablen umwandeln. Hier bietet sich die (etwas artfremde) Funktion <a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a> an, die PHP-Variablen als JSON ausgibt. Damit wird die Variable nicht nur in korrekte Anführungszeichen gesetzt (die wir wiederum nicht HTML-enkodieren dürfen), sondern erlaubt sogar die Ausgabe von komplexen Variablen wie Arrays:</p>\n<pre><code class=\"language-html\">&lt;<i>script</i>&gt;\nvar data = <b>&lt;?= json_encode($data) ?&gt;</b>;\n&lt;/<i>script</i>&gt;\n</code></pre>\n<h2 id=\"bedingungen\">Bedingungen</h2>\n<p>Die wichtigsten Kontrollstrukturen für das Templating sind Bedingungen. Hier ist das <code>if</code> der wichtigste Ansprechpartner. „Echtes“ PHP mit seinen geschweiften Klammern ist dabei aber eher unübersichtlich, da zum Beispiel bei Schachtelungen unklar sein kann, welche schließende Klammer für welchen Block zuständig ist:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($title)) { ?&gt;</b>\n  &lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n<b>&lt;?php } ?&gt;</b>\n</code></pre>\n<p>Praktischerweise <a href=\"https://www.php.net/manual/en/control-structures.alternative-syntax.php\">kennt PHP auch eine alternative Syntax für Kontrollstrukturen</a>, bei dem jeder Block hinter einem Kommando nicht mit einem <code>{</code>, sondern mit einem <code>:</code> beginnt – und statt mit einem <code>}</code> mit einem <code>end...;</code> beendet wird.</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($title)): ?&gt;</b>\n  &lt;<i>div</i>&gt;<b>&lt;?= html($title) ?&gt;</b>&lt;/<i>div</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<p>In den <code>if</code>-Bedingungen kann man dabei alle Operatoren und Funktionen verwenden, die PHP so kennt. Wichtig ist (wie immer), dass vor dem Verwenden einer Variable getestet wird, ob die Variable überhaupt definiert wird, was mit <code>!empty</code> oder <code>isset</code> geschehen kann.</p>\n<p>Darüber hinaus stehen natürlich auch <code>else</code> und <code>elseif</code> zur Verfügung:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($results)): ?&gt;</b>\n  &lt;<i>h4</i>&gt;Wir haben <b>&lt;?= html(count($results)) ?&gt;</b> Ergebnisse gefunden.&lt;/<i>h4</i>&gt;\n<b>&lt;?php else: ?&gt;</b>\n  &lt;<i>h4</i>&gt;Wir haben leider keine Ergebnisse gefunden.&lt;/<i>h4</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<h2 id=\"schleifen-und-arrays\">Schleifen und Arrays</h2>\n<p>Die Ausgabe von Listen und Tabellen ist ebenfalls eine wichtige Funktion von Templates. Mittels Schleifen kann man zum Beispiel über vorher definierte Arrays iterieren – ebenfalls mit der <a href=\"https://www.php.net/manual/en/control-structures.alternative-syntax.php\">alternative Syntax für Kontrollstrukturen</a>:</p>\n<pre><code class=\"language-html\"><b>&lt;?php if (!empty($list)): ?&gt;</b>\n  &lt;<i>ul</i>&gt;\n    <b>&lt;?php foreach($list as $index =&gt; $item): ?&gt;</b>\n      &lt;<i>li</i>&gt;<b>&lt;?= html($item) ?&gt;</b>&lt;/<i>li</i>&gt;\n    <b>&lt;?php endforeach ?&gt;</b>\n  &lt;/<i>ul</i>&gt;\n<b>&lt;?php endif ?&gt;</b>\n</code></pre>\n<p>Dabei funktionieren <code>for</code>, <code>foreach</code> und <code>while</code> ganz wunderbar:</p>\n<pre><code class=\"language-html\">&lt;<i>h4</i>&gt;Lottozahlen&lt;/<i>h4</i>&gt;\n&lt;<i>ol</i>&gt;\n  <b>&lt;?php for ($i = 1; $i &lt;= 49; $i++): ?&gt;</b>\n    &lt;<i>li</i>&gt;\n      &lt;<i>input</i> <var>type</var>=&quot;<kbd>checkbox</kbd>&quot; name=&quot;lotto_<b>&lt;?= html($i) ?&gt;</b>&quot; id=&quot;lotto_<b>&lt;?= html($i) ?&gt;</b>&quot; value=&quot;1&quot; /&gt;\n      &lt;<i>label</i> for=&quot;lotto_<b>&lt;?= html($i) ?&gt;</b>&quot;&gt;<b>&lt;?= html($i) ?&gt;</b>&lt;/<i>label</i>&gt;\n    &lt;/<i>li</i>&gt;\n  <b>&lt;?php endfor ?&gt;</b>\n&lt;/<i>ol</i>&gt;\n</code></pre>\n<h2 id=\"snippets--partials\">Snippets / Partials</h2>\n<p>Um Template-Teile wie zum Beispiel einen Header, Footer oder Navigation in ein Template einzufügen, fügen wir dem Dateinamen des Template-Teils einfach einen führenden <code>_</code> hinzu, wie zum Beispiel <code>_header.xml.php</code>.</p>\n<p>Ein Template-Teil (auch als Snippet oder Partial bekannt) kann mit dem PHP eigenen <code>include</code> ins ein anderes Template eingebunden werden:</p>\n<pre><code class=\"language-html\">&lt;!DOCTYPE html&gt;\n&lt;<i>html</i> <var>xmlns</var>=&quot;<kbd>http://www.w3.org/1999/xhtml</kbd>&quot; <var>lang</var>=&quot;<kbd>en</kbd>&quot;&gt;\n&lt;<i>head</i>&gt;\n  <b>&lt;?php include(&#39;_meta.html.php&#39;) ?&gt;</b>\n&lt;/<i>head</i>&gt;\n&lt;<i>body</i>&gt;\n  <b>&lt;?php include(&#39;_header.html.php&#39;) ?&gt;</b>\n  ...\n  <b>&lt;?php include(&#39;_footer_.html.php&#39;) ?&gt;</b>\n&lt;/<i>body</i>&gt;\n</code></pre>\n<h2 id=\"übersetzung\">Übersetzung</h2>\n<p>Sogar Übersetzungen von Templates beherrscht PHP! Die in PHP vorhandene <a href=\"https://www.php.net/manual/en/book.gettext.php\">Gettext-Bibliothek</a> erlaubt es, einen String zu übergeben, für den Gettext in einem beigefügten Wörterbuch nach der korrekten Übersetzung sucht, und diesen zurückgibt.</p>\n<p>Wichtig dafür ist das Setzen der Locale in PHP mittels <a href=\"https://www.php.net/setlocale\"><code>setlocale</code></a>:</p>\n<pre><code class=\"language-php\">setlocale(LC_MESSAGES, <kbd>'de_DE'</kbd>);\n</code></pre>\n<p>Die korrekte Bedienung dieser Funktion sorgt übrigens auch für die Übersetzung von Zahlen- und Datumsformaten, wie auch Datumsbezeichnern.</p>\n<p>Die eigentlich zu übersetzenden Bezeichner können an die <a href=\"https://www.php.net/_\">Funktion <code>gettext</code> bzw. <code>_</code></a> übergeben werden.</p>\n<pre><code class=\"language-html\"><u>&lt;!-- translate to &#39;Guten Morgen!&#39; --&gt;</u>\n&lt;<i>h4</i>&gt;<b>&lt;?= html(_(&#39;Good morning!&#39;)) ?&gt;</b>&lt;/<i>h4</i>&gt;\n</code></pre>\n<p>Die dafür notwendigen Wörterbücher kann man spielend leicht selber erstellen. Die dafür notwendigen <a href=\"http://www.gnu.org/software/gettext/manual/gettext.html#PO-Files\">PO- und MO-Dateien</a> kann man zum Beispiel mit <a href=\"https://poedit.net/\">Poedit</a> erstellen. <i>Poedit</i> ist sogar in der Lage, die gesamte PHP-Programmierung nach noch nicht katalogisierten, übersetzbaren Strings zu durchsuchen.</p>\n<p>Wenn wir mit Variablen-Ersetzung arbeiten, wird die Konstruktion etwas schwieriger:</p>\n<pre><code class=\"language-html\">&lt;<i>div</i>&gt;<b>&lt;?= html(vsprintf(_(&#39;There are %d results&#39;), [$count])) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<p>Da wir so ziemlich jeden hardcodierten Text übersetzbar machen wollen, können wir unserer simplen Template-Engine eine weitere Funktion als Abkürzung hinzufügen:</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * Alias for `vsprintf`, but with HTML escaping and translation\n */</u>\n<b>function</b> _html(<b>string</b> <var>$format</var>, <b>array</b> <var>$args</var> = []): <b>string</b>\n{\n  <i>return</i> htmlspecialchars(<var>$args</var>\n    ? vsprintf(_(<var>$format</var>), <var>$args</var>)\n    : _(<var>$format</var>)\n  );\n}\n</code></pre>\n<p>…was den Aufruf deutlich einfacher macht:</p>\n<pre><code class=\"language-html\">&lt;<i>h4</i>&gt;<b>&lt;?= _html(&#39;Good morning!&#39;) ?&gt;</b>&lt;/<i>h4</i>&gt;\n&lt;<i>div</i>&gt;<b>&lt;?= _html(&#39;There are %d results&#39;, [$count]) ?&gt;</b>&lt;/<i>div</i>&gt;\n</code></pre>\n<h2 id=\"variablen-dumping\">Variablen-Dumping</h2>\n<p>Für Entwickler wie auch Template-Designer ist es hilfreich, sich komplexe Variablen im Frontend zu Debugging-Zwecken ausgeben zu lassen. Dafür bietet PHP die Funktionen <a href=\"https://www.php.net/print_r\"><code>print_r</code></a> und <a href=\"https://www.php.net/var_dump\"><code>var_dump</code></a> / <a href=\"https://www.php.net/var_export\"><code>var_export</code></a>. Dummerweise ist in HTML ein solcher Dump schwer lesbar, da die Zeilenumbrüche in HTML ignoriert werden. Eine kleine Funktion für unsere Template-Engine kann auch diesen Missstand beheben:</p>\n<pre><code class=\"language-php\"><u>// Template.php</u>\n\n<u>/**\n * HTML dumper für PHP variables\n */</u>\n<b>function</b> debug(<var>$mixed</var>, <b>bool</b> <var>$extended</var> = <samp>false</samp>): <b>void</b>\n{\n  <i>echo</i>(<kbd>'&lt;pre class=&quot;debug&quot; style=&quot;margin: 1em 0; border: 1px solid red; background: #fee; color: #000; padding: 1em;&quot;&gt;'</kbd>);\n  <i>echo</i>(htmlspecialchars(<var>$extended</var>\n    ? var_export(<var>$mixed</var>, <samp>true</samp>)\n    : print_r(<var>$mixed</var>, <samp>true</samp>)\n  ));\n  <i>echo</i>(<kbd>'&lt;/pre&gt;'</kbd>);\n}\n</code></pre>\n<p>Damit können auch komplexe Variablen bis hin zu Objekten lesbar ausgegeben werden:</p>\n<pre><code class=\"language-html\"><b>&lt;?php debug($data) ?&gt;</b>\n</code></pre>\n<p>Statt einer weithin sichtbaren Debug-Ausgabe könnte man natürlich gleich auf die <a href=\"https://journal.3960.org/posts/einfachste-php-objekt-debugger-fuer-browser/\">Ausgabe von Debugging-Daten in der Browser-Console</a> ausweichen.</p>\n<h2 id=\"ausgabe-von-anderen-content-typen\">Ausgabe von anderen Content-Typen</h2>\n<p>PHP erzeugt von Haus aus eine HTML-Ausgabe. Im Browser funktioniert dies so, dass jedes PHP-Skript den Webserver anweist, als MIME-Type <code>text/html</code> zurückzugeben. Tatsächlich kann PHP aber auch ganz andere MIME-Types ausgeben. Dazu gibt es die Funktion <a href=\"https://www.php.net/header\"><code>header</code></a>, mit der PHP den Webserver anweisen kann, bestehende Header zu ändern bzw. neue zu setzen. Der für uns interessante Header ist <code>Content-Type</code>.</p>\n<p>So können wir PHP anweisen, <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types\">jeden erdenklichen MIME-Type</a> zurückzugeben.</p>\n<h3 id=\"xml\">XML</h3>\n<p>Für XML verwenden wir die Template-Endung <code>.xml.php</code>, also zum Beispiel <code>sitemap.xml.php</code>. Diese Templates unterscheiden sich tatsächlich nicht großartig von HTML-Templates, und verwenden sogar die selben Quoting/Escaping-Funktionen. Nur der zusätzliche <code>Content-Type</code>-Header muss mitgeschickt werden:</p>\n<pre><code class=\"language-xml\"><b>&lt;?php header(&#39;Content-Type: text/xml&#39;); ?&gt;</b>\n&lt;<i>urlset</i>&gt;\n  <b>&lt;?php foreach($urls as $url): ?&gt;</b>\n    &lt;<i>url</i>&gt;\n      &lt;<i>loc</i>&gt;<b>&lt;?= html($url) ?&gt;</b>&lt;/<i>loc</i>&gt;\n    &lt;/<i>url</i>&gt;\n  <b>&lt;?php endforeach ?&gt;</b>\n&lt;/<i>urlset</i>&gt;\n</code></pre>\n<h3 id=\"json\">JSON</h3>\n<p>Auch JSON kann PHP für uns ausgeben. Hierfür verwenden wir die Template-Endung <code>.json.php</code>, wie zum Beispiel <code>feed.json.php</code>, und senden wieder den korrekten <code>Content-Type</code>. Um das Quoting/Escaping in den Griff zu bekommen, bauen wir unser JSON nicht von Hand, sondern verwenden die Funktion <a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a>, die auch strukturierte PHP-Variablen strukturiert und proper formatiert ausspuckt:</p>\n<pre><code class=\"language-php\">&lt;?php\n  header(<kbd>'Content-Type: application/json'</kbd>);\n  <i>echo</i>(json_encode(<var>$data</var>));\n</code></pre>\n<p>Für besonders schönes JSON kann man übrigens den Parameter <code>JSON_PRETTY_PRINT</code> mitgeben:</p>\n<pre><code class=\"language-php\">&lt;?php\n  header(<kbd>'Content-Type: application/json'</kbd>);\n  <i>echo</i>(json_encode(<var>$data</var>, JSON_PRETTY_PRINT));\n</code></pre>\n<p>Übrigens respektiert die Funktion <code>json_encode</code> tatsächlich PHP-Typen. Damit werden PHP-Integer tatsächlich als JSON-Integer ausgegeben, und PHP-Strings als JSON-Strings. Hier muss also in PHP den Unterschied zwischen <code>1</code> und <code>„1“</code> berücksichtigt werden.</p>\n<h3 id=\"hol-das-richtige-template\">Hol das richtige Template</h3>\n<p>Die obigen Beispiele zum Setzen des korrekten MIME-Types und Holen der Templates kann man natürlich auch allgemein lösen:</p>\n<pre><code class=\"language-php\"><u>// e.g. $template = 'index';</u>\n<u>// e.g. $contentType = 'html';</u>\n\n<i>switch</i> (<var>$contentType</var>) {\n  <i>case</i> <kbd>'xml'</kbd>:\n    header(<kbd>'Content-Type: text/xml'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'json'</kbd>:\n    header(<kbd>'Content-Type: application/json'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'txt'</kbd>:\n    header(<kbd>'Content-Type: text/plain'</kbd>);\n    <i>break</i>;\n  <i>case</i> <kbd>'csv'</kbd>:\n    header(<kbd>'Content-Type: text/csv'</kbd>);\n    <i>break</i>;\n  default:\n    <var>$contentType</var> = <kbd>'html'</kbd>;\n    <i>break</i>;\n}\n\n<var>$templateFilename</var> = __DIR__ . <kbd>'/templates/'</kbd> . <var>$template</var> . <kbd>'.'</kbd> . <var>$contentType</var> . <kbd>'.php'</kbd>;\n<i>require</i>(<var>$templateFilename</var>);\n</code></pre>\n<h2 id=\"fazit\">Fazit</h2>\n<p>Jedes PHP-Projekt braucht eine saubere Trennung zwischen Logik und Ausgabe – aber für diese Trennung braucht es nicht immer eine Template-Engine wie <a href=\"https://twig.symfony.com/\">Twig</a> oder <a href=\"https://www.smarty.net/\">Smarty</a>. Auch handelsübliches PHP kann (mit wenigen, kleinen Helferlein) eine performante, wartungsarme Alternative fürs Templating sein.</p>\n<p>Hier nochmals eine Übersicht über <a href=\"https://www.php.net/manual/de/ref.strings.php\">die wichtigsten PHP-Funktionen für Templating</a>:</p>\n<div class=\"table-wrapper\"><table>\n<thead>\n<tr>\n<th>Funktion</th>\n<th>Alias</th>\n<th>Beschreibung</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><a href=\"https://www.php.net/htmlspecialchars\"><code>htmlspecialchars</code></a></td>\n<td class=\"tag-code\"><code>html</code></td>\n<td>HTML-Escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/urlencode\"><code>urlencode</code></a></td>\n<td>-</td>\n<td>URL-Escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/json_encode\"><code>json_encode</code></a></td>\n<td>-</td>\n<td>JSON/JavaScript-Escaping</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/setlocale\"><code>setlocale</code></a></td>\n<td>-</td>\n<td>Setzen der Sprache für Übersetzungen</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/_\"><code>gettext</code> / <code>_</code></a></td>\n<td class=\"tag-code\"><code>_html</code></td>\n<td>Ausgabe von Übersetzungen</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/strftime\"><code>strftime</code></a></td>\n<td>-</td>\n<td>Gibt ein lokales Datumsformat aus</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/localeconv\"><code>localeconv</code></a></td>\n<td>-</td>\n<td>Ermittelt die lokale Ausgabeeinstellungen für Zahlen</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/printf\"><code>printf</code></a></td>\n<td class=\"tag-code\"><code>_html</code></td>\n<td>Ausgabe von Variablen in Strings</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/nl2br\"><code>nl2br</code></a></td>\n<td>-</td>\n<td>Konvertiert Zeilenumbrüche in HTML-<code>&lt;br /&gt;</code></td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/implode\"><code>implode</code></a></td>\n<td>-</td>\n<td>Verbindet ein Array zu einem String</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/var_dump\"><code>var_dump</code></a></td>\n<td class=\"tag-code\"><code>debug</code></td>\n<td>Dump von PHP-Variablen</td>\n</tr>\n<tr>\n<td><a href=\"https://www.php.net/header\"><code>header</code></a></td>\n<td>-</td>\n<td>Ausgabe von anderen <code>Content-Type</code></td>\n</tr>\n</tbody></table></div>\n<p>Einzig beim Thema Vererbung von Templates sind echte Template-Engines deutlich komfortabler, wie <a href=\"https://twig.symfony.com/doc/3.x/templates.html#template-inheritance\">Twigs Template-Vererbung</a> und <a href=\"https://www.smarty.net/docs/en/advanced.features.template.inheritance.tpl\">Smartys Template-Vererbung</a> zeigen. Siehe dazu auch den <a href=\"https://css-tricks.com/comparing-html-preprocessor-features/\">Vergleich der Templating-Engines für PHP auf <i>CSS-Tricks</i></a>.</p>\n<hr />\n<p>There also is an <a href=\"https://journal.3960.org/posts/2021-12-17-world-smallest-php-templating-engine/\" rel=\"alternate\">English version of this article, „World&#39;s smallest template engine“</a>.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Die%20kleinste%20PHP-Templating-Engine%20der%20Welt&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2020-08-20-kleinste-php-templating-engine-welt%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/2020-08-20-kleinste-php-templating-engine-welt/",
        "pubDate": "Thu, 20 Aug 2020 18:02:28 +0200",
        "atom_published": "2020-08-20T18:02:28+02:00",
        "atom_updated": "2022-02-21T18:22:25+01:00",
        "guid": "user/posts/2020-08-20-kleinste-php-templating-engine-welt/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Webdevelop"
        ]
      },
      {
        "title": "Programmierung: Zurück zur Werkbank",
        "description": "<p>Wie schon in dem Artikel <a href=\"https://journal.3960.org/posts/2019-04-23-einfachheit-programmierung/\">„Simple &amp; Boring“</a> von Chris Coyier hat auch Bastian Allgeier eine Lanze für Einfachheit in der Programmierung gebrochen. Sein Artikel <a href=\"https://bastianallgeier.com/notes/simplicity-part-2\">„Simplicity (II)“</a> dürfte vielen altgedienten Programmierern aus der Seele sprechen.</p>\n<p>Tatsächlich bemerke ich sowohl in der privaten als auch beruflichen Programmierung den Trend, für mehr Geschwindigkeit ein neues Tool einzusetzen… das kleine Probleme verursacht, die durch ein weiteres Tool gelöst werden müssen… das kleine Probleme verursacht, die durch ein weiteres Tool gelöst werden müssen…</p>\n<figure class=\"blockquote\"><blockquote cite=\"https://bastianallgeier.com/notes/simplicity-part-2\"><p>When everything works, it feels like magic. When something breaks, it&#39;s hell.</p></blockquote>\n<figcaption><a href=\"https://bastianallgeier.com/notes/simplicity-part-2\">Bastian Allgeier, „<cite>Simplicity (II)</cite>“</a></figcaption></figure>\n<p>Der Artikel dreht sich zwar primär darum, was diese Abhängigkeiten gerade für ältere Projekte bedeuten (nämlich, dass Abhängigkeiten nach ein paar Jahren sich nicht wieder auslösen lassen, weil die dafür benötigten Versionen an Tools nicht mehr zur Verfügung stehen), inzwischen bemerke ich aber auch bei aktuellen Projekten die Probleme, die übermäßige Abhängigkeiten für die Entwicklungsgeschwindigkeit bedeuten können, wenn auch nur ein Teil ausfällt.</p>",
        "content_encoded": "<p>Wie schon in dem Artikel <a href=\"https://journal.3960.org/posts/2019-04-23-einfachheit-programmierung/\">„Simple &amp; Boring“</a> von Chris Coyier hat auch Bastian Allgeier eine Lanze für Einfachheit in der Programmierung gebrochen. Sein Artikel <a href=\"https://bastianallgeier.com/notes/simplicity-part-2\">„Simplicity (II)“</a> dürfte vielen altgedienten Programmierern aus der Seele sprechen.</p>\n<p>Tatsächlich bemerke ich sowohl in der privaten als auch beruflichen Programmierung den Trend, für mehr Geschwindigkeit ein neues Tool einzusetzen… das kleine Probleme verursacht, die durch ein weiteres Tool gelöst werden müssen… das kleine Probleme verursacht, die durch ein weiteres Tool gelöst werden müssen…</p>\n<figure class=\"blockquote\"><blockquote cite=\"https://bastianallgeier.com/notes/simplicity-part-2\"><p>When everything works, it feels like magic. When something breaks, it&#39;s hell.</p></blockquote>\n<figcaption><a href=\"https://bastianallgeier.com/notes/simplicity-part-2\">Bastian Allgeier, „<cite>Simplicity (II)</cite>“</a></figcaption></figure>\n<p>Der Artikel dreht sich zwar primär darum, was diese Abhängigkeiten gerade für ältere Projekte bedeuten (nämlich, dass Abhängigkeiten nach ein paar Jahren sich nicht wieder auslösen lassen, weil die dafür benötigten Versionen an Tools nicht mehr zur Verfügung stehen), inzwischen bemerke ich aber auch bei aktuellen Projekten die Probleme, die übermäßige Abhängigkeiten für die Entwicklungsgeschwindigkeit bedeuten können, wenn auch nur ein Teil ausfällt.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Programmierung%3A%20Zur%C3%BCck%20zur%20Werkbank&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2019-09-13-programmierung-zurueck-zur-werkbank%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://bastianallgeier.com/notes/simplicity-part-2",
        "pubDate": "Fri, 13 Sep 2019 18:23:11 +0200",
        "atom_published": "2019-09-13T18:23:11+02:00",
        "atom_updated": "2019-10-17T18:51:03+02:00",
        "guid": "user/posts/2019-09-13-programmierung-zurueck-zur-werkbank/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "CSS",
          "Javascript",
          "Meinung",
          "PHP",
          "Programmierung",
          "Webdevelop",
          "Geckobar"
        ]
      },
      {
        "title": "Der kleinste YAML-Konverter der Welt",
        "description": "<p>Immer wieder haben wir in der Programmierung die Herausforderung, strukturierte Daten von Nutzern eingeben zu lassen. Ein wunderbares Format dafür ist <a href=\"http://yaml.org/\">YAML</a>.</p>",
        "content_encoded": "<p>Immer wieder haben wir in der Programmierung die Herausforderung, strukturierte Daten von Nutzern eingeben zu lassen. Ein wunderbares Format dafür ist <a href=\"http://yaml.org/\">YAML</a>.</p>\n<!-- more -->\n<p id=\"more\"><abbr title=\"YAML Ain't Markup Language\">YAML</abbr> hat den Vorteil, dass es auch von unversierten Nutzern eingegeben werden kann – ein Vorteil gegenüber <abbr title=\"Extensible Markup Language\">XML</abbr> oder <abbr title=\"JavaScript Object Notation\">JSON</abbr>, und auch INI ist nicht unbedingt das einfachste Format, wenn es um eine entspannte Leerzeichenhandhabung geht.</p>\n<p>So ist die Eingabe und auch Speicherung von YAML eigentlich trivial. Eine <code>&lt;textarea /&gt;</code> im Browser, ein Textfeld in der Datenbank, und ein YAML-Parser zum Umwandeln der Daten in benutzbare Variablen – fertig ist die Konfigurationsmöglichkeit.</p>\n<p>YAML-Parser gibt es eigentlich für so ziemlich jede Sprache, wenn auch zumeist nicht als integralen Bestandteil. So muss man z.B. bei PHP weitere Bibliotheken installieren.</p>\n<h2 id=\"der-einfachste-yaml-fall\">Der einfachste YAML-Fall</h2>\n<p>Für Konfigurationen reicht zumeist ein einfache Untermenge von YAML aus: Eine Sammlung von <code>key: value</code>-Zuordnungen, sogenannte <i>Block Mappings</i>. Diese erlauben tabellarische Zuordnung von strukturierten, schemalosen Daten. Für meinen Fall gibt es eine weitere Einschränkung: Die Werte dürfen nur einzeilig sein:</p>\n<pre><code class=\"language-yaml\"><i>en_GB</i>: https://www.example.co.uk/\n<i>fr_FR</i>: https://www.example.fr/\n<i>de_DE</i>: https://www.example.de/\n</code></pre>\n<p>Um den Aufwand für einen echten YAML-Parser zu vermeiden, habe ich mir für exakt diesen sehr einfachen Anwendungsfall einen maximal-einfachen Parser gebaut, der <i>Block Mappings</i> konvertiert.</p>\n<h3 id=\"in-php\">…in PHP</h3>\n<p>Der Konverter erzeugt ein strukturiertes, assoziatives Array:</p>\n<pre><code class=\"language-php\"><u>/**\n * Convert lines like `a: b` into an associative array like `['a' =&gt; 'b']`.\n * @param  string $string\n * @return array  \n */</u>\n<b>function</b> parseSimpleYaml(<b>string</b> <var>$yamlString</var>): <b>array</b>\n{\n    <i>return</i> preg_match_all(<kbd>'/(?:^|\\n)\\s*([^#].+?)\\s*:\\s*([^\\n#]+)/'</kbd>, trim(<var>$yamlString</var>), <var>$matches</var>)\n        ? array_combine(<var>$matches</var>[<em>1</em>], <var>$matches</var>[<em>2</em>])\n        : [<var>$yamlString</var>]\n    ;\n}\n</code></pre>\n<p>Damit wird aus dem o.a. YAML <i>Block Mapping</i> ein assoziatives Array:</p>\n<pre><code class=\"language-php\">[\n    <kbd>'en_GB'</kbd> =&gt; <kbd>'https://www.example.co.uk/'</kbd>,\n    <kbd>'fr_FR'</kbd> =&gt; <kbd>'https://www.example.fr/'</kbd>,\n    <kbd>'de_DE'</kbd> =&gt; <kbd>'https://www.example.de/'</kbd>,\n]\n</code></pre>\n<h3 id=\"in-javascript\">…in JavaScript</h3>\n<p>Eine ähnliche Methode kann man in JavaScript anwenden:</p>\n<pre><code class=\"language-javascript\"><b>const</b> parseSimpleYaml = <b>function</b>(yamlString) {\n  <b>let</b> yamlObject = {};\n  yamlString\n    .<i>split</i>(<kbd>&quot;\\n&quot;</kbd>)\n    .<i>map</i>((i) <samp>=&gt;</samp> {\n      <i>return</i> i.<b>match</b>(/^([^<u>#].+?)\\s*:\\s*([^\\n#]+)/);</u>\n    })\n    .<i>filter</i>((i) <samp>=&gt;</samp> {\n      <i>return</i> i !== <samp>null</samp>\n    })\n    .<i>forEach</i>((i) <samp>=&gt;</samp> {\n      yamlObject[i[<em>1</em>]] =  i[<em>2</em>];\n    })\n  ;\n  <i>return</i> yamlObject;\n}\n</code></pre>\n<p>Damit wird aus dem o.a. YAML <i>Block Mapping</i> ein assoziatives Objekt:</p>\n<pre><code class=\"language-javascript\">{\n  <kbd>'en_GB'</kbd>: <kbd>'https://www.example.co.uk/'</kbd>,\n  <kbd>'fr_FR'</kbd>: <kbd>'https://www.example.fr/'</kbd>,\n  <kbd>'de_DE'</kbd>: <kbd>'https://www.example.de/'</kbd>,\n}\n</code></pre>\n<h2 id=\"der-selbe-fall-im-ini-format\">Der selbe Fall im INI-Format</h2>\n<p>Ehrlich gesagt könnte man für o.a. einfaches Speicherformat auch ein vereinfachtes INI-Format verwenden:</p>\n<pre><code class=\"language-ini\"><i>en_GB</i>=<kbd>https://www.example.co.uk/</kbd>\n<i>fr_FR</i>=<kbd>https://www.example.fr/</kbd>\n<i>de_DE</i>=<kbd>https://www.example.de/</kbd>\n</code></pre>\n<p>Strukturell funktioniert das exakt gleich, nur dass der zum Auslesen verwendete reguläre Ausdruck nun wie folgt aussieht:<br /><code>/(?:^|\\n)\\s*([^;].+?)\\s*=\\s*([^\\n;]+)/</code></p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Der%20kleinste%20YAML-Konverter%20der%20Welt&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2018-11-26-kleinste-yaml-konverter-welt%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/2018-11-26-kleinste-yaml-konverter-welt/",
        "pubDate": "Mon, 26 Nov 2018 19:09:20 +0100",
        "atom_published": "2018-11-26T19:09:20+01:00",
        "atom_updated": "2020-09-21T12:19:58+02:00",
        "guid": "user/posts/2018-11-26-kleinste-yaml-konverter-welt/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Webdevelop"
        ]
      },
      {
        "title": "Der Umstieg auf Microsoft Visual Studio Code",
        "description": "<p><img src=\"https://journal.3960.org/posts/2018-09-19-umstieg-auf-microsoft-visual-studio-code/visual-code-400x225.png\" class=\"default\" width=\"400\" height=\"225\" style=\"--aspect-ratio: 16/9;\" alt=\"\" /></p>\n<p>Mein Kollege Slawo hat mich schon vor geraumer Zeit auf einen interessanten Code-Editor hingewiesen: Microsofts <i>Visual Studio Code</i> (oder kurz „VS Code“). Microsofts Charme-Offensive in Richtung der Developer-Community hat ein ganz neues Werkzeug hervorgebracht, dass es mit den etablierten Konkurrenten vom Range eines Sublime Text, Atom und Co aufnehmen soll.</p>",
        "content_encoded": "<p><img src=\"https://journal.3960.org/posts/2018-09-19-umstieg-auf-microsoft-visual-studio-code/visual-code-400x225.png\" class=\"default\" width=\"400\" height=\"225\" style=\"--aspect-ratio: 16/9;\" alt=\"\" /></p>\n<p>Mein Kollege Slawo hat mich schon vor geraumer Zeit auf einen interessanten Code-Editor hingewiesen: Microsofts <i>Visual Studio Code</i> (oder kurz „VS Code“). Microsofts Charme-Offensive in Richtung der Developer-Community hat ein ganz neues Werkzeug hervorgebracht, dass es mit den etablierten Konkurrenten vom Range eines Sublime Text, Atom und Co aufnehmen soll. </p>\n<!-- more -->\n<h2 id=\"more\">Warum wechseln?</h2>\n<p>Lange Jahre war <a href=\"https://www.sublimetext.com/3\" rel=\"nomention\">Sublime Text 2 bzw. 3</a> mein Lieblings-Code-Editor. Vor allen Dingen die hohe Geschwindigkeit beim Tippen als auch die unglaublich schnelle Suche machten für mich lange Jahre die Benutzung einer echten IDE überflüssig. Mit Hilfe von unzähligen <a href=\"https://journal.3960.org/posts/meine-einstellungen-in-sublime-text-3/\">Sublime-Plugins</a> habe ich mich da inzwischen sehr gemütlich eingerichtet.</p>\n<p>Beruflich bin ich aber inzwischen bei <a href=\"https://www.jetbrains.com/phpstorm/\" rel=\"nomention\">PhpStorm</a> angelangt, und habe mich da an bestimmten Luxus gewöhnt. Hervorragendes Auto-Complete bzw. die Verlinkung von Objekten im Quellcode, die direkte Anzeige von Fehlern oder die Integration von SQL, Git und Terminals vermisste man dann und wann doch in Sublime Text. Dafür war mir bei PhpStorm schon immer seine Behäbigkeit und sein Ressourcenhunger für das heimische Arbeiten ein Gräuel.</p>\n<p>Wonach ich also suchte ist die Spritzigkeit von Sublime Text 3, und die integrierte Arbeitsweise und Unterstützung von PhpStorm. Für mich selber habe ich mit <a href=\"https://code.visualstudio.com/\" rel=\"nomention\">Microsoft Visual Studio Code</a> den perfekten Kompromiss gefunden – zumal dieser Kompromiss keinen Cent kostet.</p>\n<blockquote><p>Sublime Text 3 ist der schnellste Klappspaten unter den Editoren, während PhpStorm das Äquivalent eines Braunkohletagebaubaggers ist.<br />Damit entspricht Microsofts Visual Studio Code einem Bobcat-Bagger.</p></blockquote>\n<p>Meine Highlights bei VS Code:</p>\n<ul>\n<li>Schnell, kleiner Fußabdruck</li>\n<li>Auto-Complete, Auto-Suggest, bzw. Intellisense</li>\n<li>Integriertes Git</li>\n<li>Integriertes Terminal</li>\n<li>Direkte Unterstützung für Gulp, Grunt, Docker, etc.</li>\n<li>Integriertes Debugging</li>\n<li>Schnell wachsende Palette an Plugins</li>\n</ul>\n<h2 id=\"meine-ersten-eindrücke\">Meine ersten Eindrücke</h2>\n<p>Persönlich wechsele ich ungern meine Werkzeuge aus, wenn ich nicht einen echten Grund dafür verspüre. Ich werfe gerne ein Auge auf die Trends in diesem Bereich, ohne andauernd den Drang zu verspüren, alles selber ausprobieren zu müssen. Außerdem möchte ich eigentlich wenig Zeit mit meinen Werkzeugen, sondern lieber mit meiner Arbeit verbringen. Umso größer ist für mich also der Schritt, meinen Code-Editor auszutauschen – das zentrale Werkzeug, dass meine Ideen in ausführbaren Code verwandelt.</p>\n<p>Wie bei jedem Editor-Wechsel war am Anfang etwas Fremdeln angesagt. <a href=\"https://css-tricks.com/on-switching-code-editors/\">Chris Coyiers Artikel über den Umstieg auf VS Code</a> hat mir zum Glück gerade noch rechtzeitig die Möglichkeit erläutert, mit einem Plugin das Verhalten von VS Code in einigen relevanten Bereichen (z.B. Tastaturkürzel) einfach auf das Verhalten von Sublime Text 3 abzuändern.</p>\n<p>VS Code nutze ich daheim aktuell für folgende Projekt-Typen:</p>\n<ul>\n<li>Node.js</li>\n<li>PHP / Web-Projekte</li>\n</ul>\n<p>Überraschenderweise ist <a href=\"https://code.visualstudio.com/\" rel=\"nomention\">VS Code für Windows, Mac OSX <em>und</em> Linux verfügbar</a>.</p>\n<h2 id=\"plugins-bzw-extensions\">Plugins bzw. Extensions</h2>\n<p>Direkt nach der Installation stellt man überrascht fest, dass es eigentlich nicht viele Plugins braucht; VS Code hat unglaublich viele Funktionalitäten von vorne herein integriert. So ist für die Verwendung von Gulp, Grunt, Sass, ESlint, Php-Debuggern und vielen anderen Notwendigkeiten des Webentwickler-Lebens bereits Vorsorge getroffen. Selbst eine Live-Preview von Markdown, ein Terminal und Git ist integriert. Daher ist meine persönliche Liste von zusätzlichen Plugins relativ bescheiden:</p>\n<dl>\n<dt>Sublime Text Keymap and Settings</dt><dd> Die wahre Geschwindigkeit eines Programmierers besteht aus zwei Komponenten: Der Beherrschung der jeweiligen Programmiersprache, und der Beherrschung seiner Werkzeuge. Da viele Tastenkombinationen aus Sublime Text 3 bei mir inzwischen ins Rückenmark übergegangen sind, würde eine Umstellung mich deutlich verlangsamen. Gott sei dank gibt es aber ein Plugin für VS Code, mit dem die Tastaturkürzel von Sublime Text 3 auch in VS Code funktionieren.</dd>\n<dt>EditorConfig for VS Code</dt><dd> In jedem Editor, den ich benutze, verwende ich <a href=\"https://editorconfig.org/\" rel=\"nomention\">EditorConfig</a>. Damit habe ich die jeweils projektspezifischen Einstellungen für Tabulatoren, Zeichensätze und Zeilenenden automatisch richtig, ohne mich selber um die Details kümmern zu müssen.</dd>\n<dt>Prettier</dt><dd> Dieses Plugin formatiert so ziemlich jeden Quellcode nach den Ideen von <a href=\"https://prettier.io/\">Prettier</a> um, und sorgt so auf Knopfdruck für Ordnung.</dd>\n<dt>Spell Right</dt><dd> Da ich auch viele Texte in meinem Editor schreibe, ist eine anständige Rechtschreibkorrektur überlebenswichtig. Dieses Plugin benutzt einfach das im Betriebssystem verankerte Wörterbuch.</dd>\n<dt>sftp</dt><dd> Erlaubt den direkten Zugriff auf entfernte Verzeichnisse. U.a. kann man Dateien hin- und herkopieren.</dd>\n<dt>Sort Lines</dt><dd> Tut, was man erwartet – es sortiert Zeilen.</dd>\n<dt>Git History</dt><dd> Eine grafische Übersicht über Commits im aktuellen Git-Repo.</dd>\n<dt>PHP Doc</dt><dd> Eine vernünftige Inline-Dokumentation ist ein Muss. Mit diesem Plugin kann man durch Eintippen von <code>/**</code> automatisch für den dahinter liegenden Code-Abschnitt einen PhpDoc-Kommentar erzeugen lassen.</dd>\n<dt>ESLint</dt><dd> Überprüft Javascript (z.B. für NodeJS) mit der Hilfe von <a href=\"https://eslint.org/\" rel=\"nomention\">ESLint</a> und zeigt Fehler und Vorschläge direkt im Editor an.</dd>\n<dt>Apache Conf</dt><dd> Ein Syntax-Highlighter für Apache-Konfigurationen ist in VS Code nicht direkt enthalten – aber als Plugin nachrüstbar.</dd>\n<dt>Docker</dt><dd> Erweitert VS Code um einen Docker-Tab für laufende Docker-Container, und fügt Docker-Befehle hinzu.</dd>\n</dl>\n<p>Für viele verschiedene Programmieraufgaben stellt Microsoft <a href=\"https://code.visualstudio.com/docs\" rel=\"nomention\">Tutorials für VS Code</a> bereit. Die Palette der <a href=\"https://code.visualstudio.com/docs/languages/overview\" rel=\"nomention\">im Standard unterstützten Sprachen von VS Code</a> reicht dabei von HTML, CSS, JavaScript und PHP über Node.js hin zu C#, C/C++, ohne das diese Liste erschöpfend <em>alle</em> unterstützten Sprachen enthält.</p>\n<p>In Kombination mit der Erweiterbarkeit und der Möglichkeit, Kommandozeilentools in VS Code einzubinden, steht dem geneigten Entwickler so ziemlich jede Sprache bequem zur Verfügung.</p>\n<p>Übrigens sieht <a href=\"https://code.visualstudio.com/blogs/2017/11/15/live-share\">Visual Studio Live Share</a> sehr spannend aus: Hier können mehrere Programmierer zusammen in Echtzeit ein und dieselbe Datei bearbeiten. Das habe ich persönlich zwar noch nicht ausprobiert, Youtube-Videos davon sehen aber <em>sehr</em> interessant aus:</p>\n<div class=\"video-player video-player--youtube\"><iframe allowfullscreen=\"allowfullscreen\" title=\"https://www.youtube.com/watch?v=1TJyopGhfiU\" src=\"https://www.youtube-nocookie.com/embed/1TJyopGhfiU?enablejsapi=1\" srcdoc=\"&lt;style&gt;*{padding:0;margin:0;overflow:hidden}html,body{height:100%}img,span{position:absolute;width:100%;top:0;bottom:0;margin:auto}span{height:1.5em;text-align:center;font:48px/1.5 sans-serif;color:white;text-shadow:0 0 0.5em black}&lt;/style&gt;&lt;a href=&quot;https://www.youtube.com/embed/1TJyopGhfiU?autoplay=1&quot;&gt;&lt;img src=&quot;https://img.youtube.com/vi/1TJyopGhfiU/hqdefault.jpg&quot; alt=&quot;&quot;&gt;&lt;span&gt;▶&lt;/span&gt;&lt;/a&gt;\"></iframe><!-- img src=\"https://img.youtube.com/vi/1TJyopGhfiU/hqdefault.jpg\" --></div>\n<h2 id=\"einstellungen\">Einstellungen</h2>\n<p>Meine persönlichen Einstellungen in der <code>settings.json</code>…</p>\n<pre><code class=\"language-json\">{\n  <b>&quot;editor.fontSize&quot;</b>: <em>13</em>,\n  <b>&quot;editor.formatOnPaste&quot;</b>: <samp>false</samp>,\n  <b>&quot;editor.formatOnSave&quot;</b>: <samp>false</samp>,\n  <b>&quot;editor.largeFileOptimizations&quot;</b>: <samp>false</samp>,\n  <b>&quot;editor.multiCursorModifier&quot;</b>: <kbd>&quot;ctrlCmd&quot;</kbd>,\n  <b>&quot;editor.renderWhitespace&quot;</b>: <kbd>&quot;boundary&quot;</kbd>,\n  <b>&quot;editor.rulers&quot;</b>: [<em>80</em>, <em>120</em>],\n  <b>&quot;editor.snippetSuggestions&quot;</b>: <kbd>&quot;top&quot;</kbd>,\n  <b>&quot;editor.tabSize&quot;</b>: <em>2</em>,\n  <b>&quot;editor.useTabStops&quot;</b>: <samp>false</samp>,\n  <b>&quot;editor.wordWrap&quot;</b>: <kbd>&quot;on&quot;</kbd>,\n  <b>&quot;explorer.confirmDelete&quot;</b>: <samp>false</samp>,\n  <b>&quot;files.autoSave&quot;</b>: <kbd>&quot;onFocusChange&quot;</kbd>,\n  <b>&quot;files.eol&quot;</b>: <kbd>&quot;\\n&quot;</kbd>,\n  <b>&quot;files.exclude&quot;</b>: {\n      <b>&quot;.idea&quot;</b>: <samp>true</samp>,\n      <b>&quot;.vscode&quot;</b>: <samp>true</samp>\n  },\n  <b>&quot;git.autofetch&quot;</b>: <samp>true</samp>,\n  <b>&quot;git.enableSmartCommit&quot;</b>: <samp>true</samp>,\n  <b>&quot;git.path&quot;</b>: <kbd>&quot;C:\\\\Program Files\\\\Git\\\\bin\\\\git.exe&quot;</kbd>,\n  <b>&quot;npm.enableScriptExplorer&quot;</b>: <samp>true</samp>,\n  <b>&quot;spellright.documentTypes&quot;</b>: [\n      <kbd>&quot;markdown&quot;</kbd>,\n      <kbd>&quot;latex&quot;</kbd>,\n      <kbd>&quot;plaintext&quot;</kbd>\n  ],\n  <b>&quot;terminal.integrated.shell.windows&quot;</b>: <kbd>&quot;C:\\\\Program Files\\\\Git\\\\bin\\\\bash.exe&quot;</kbd>,\n  <b>&quot;workbench.colorTheme&quot;</b>: <kbd>&quot;Monokai&quot;</kbd>,\n  <b>&quot;php.validate.executablePath&quot;</b>: <kbd>&quot;C:\\\\xampp\\\\php\\\\php.exe&quot;</kbd>,\n  <b>&quot;editor.defaultFormatter&quot;</b>: <kbd>&quot;esbenp.prettier-vscode&quot;</kbd>\n}\n</code></pre>\n<p>…und der <a href=\"https://code.visualstudio.com/docs/getstarted/keybindings\" rel=\"nomention\"><code>keybindings.json</code></a>…</p>\n<pre><code class=\"language-json\">[\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+g&quot;</kbd>,          <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.nextMatchFindAction&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+7&quot;</kbd>,          <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.commentLine&quot;</kbd>, <b>&quot;when&quot;</b>: <kbd>&quot;editorTextFocus &amp;&amp; !editorReadonly&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+shift+7&quot;</kbd>,    <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.blockComment&quot;</kbd>, <b>&quot;when&quot;</b>: <kbd>&quot;editorFocus&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+shift+d&quot;</kbd>,    <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.copyLinesDownAction&quot;</kbd>, <b>&quot;when&quot;</b>: <kbd>&quot;editorFocus&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+d&quot;</kbd>,          <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.addSelectionToNextFindMatch&quot;</kbd>, <b>&quot;when&quot;</b>: <kbd>&quot;editorFocus&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+h&quot;</kbd>,          <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.startFindReplaceAction&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+shift+up&quot;</kbd>,   <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.moveLinesUpAction&quot;</kbd> },\n  { <b>&quot;key&quot;</b>: <kbd>&quot;ctrl+shift+down&quot;</kbd>, <b>&quot;command&quot;</b>: <kbd>&quot;editor.action.moveLinesDownAction&quot;</kbd> }\n]\n</code></pre>\n<p>…sind direkte Übernahmen des Verhaltens, dass ich mir schon für <a href=\"https://journal.3960.org/posts/meine-einstellungen-in-sublime-text-3/\">Sublime Text 3</a> überlegt hatte. Viel mehr Konfiguration ist dann nicht mehr nötig.</p>\n<h3 id=\"fallstrick-git-unter-windows\">Fallstrick: Git unter Windows</h3>\n<p>Bei meinen ersten Versuchen scheiterte ich daran, den in VS Code integrierten Git-Client zum Laufen zu bringen. Obwohl auf der Kommandozeile und auch in einem lokal installierten Source Tree alles ganz wunderbar lief, wehrte sich VS Code gegen <code>git push</code> und <code>git pull</code> mit dem Hinweis, dass der Git-Server meine Credentials nicht mochte. Die Lösung dafür war aber gar nicht so komplex:</p>\n<p>Die Voraussetzungen für die Benutzung von Git direkt aus Microsofts Visual Studio Code ist die Installation von <a href=\"https://github.com/git-for-windows/git/releases\" rel=\"nomention\">Git Credential Manager</a> (bei neuerem Git wird dies automatisch mit dabei sein) und <a href=\"https://www.chiark.greenend.org.uk/~sgtatham/putty/\" rel=\"nomention\">Plink von Putty</a>, dass den SSH-Schlüssel für Git bereitstellt.</p>\n<p>Außerdem sollte eine globale Systemvariable vorhanden sein, die auf <code>plink.exe</code> verweist. Das kann man mit der Windows Eingabeaufforderung erledigen:</p>\n<pre><code class=\"language-batch\"><b>set</b> GIT_SSH=<kbd>&quot;C:\\Program Files\\PuTTY\\plink.exe&quot;</kbd>\n<b>setx</b> GIT_SSH <var>%GIT_SSH%</var>\n<b>echo</b> <var>%GIT_SSH%</var>\n</code></pre>\n<p>Sobald der SSH-Schlüssel in Pageant geladen ist, kann VS Code auch problemlos alle Git-Kommandos ausführen.</p>\n<h2 id=\"fazit\">Fazit</h2>\n<p>Für meine heimische Arbeit ist Microsofts Visual Studio Code der würdige Nachfolger von Sublime Text 3. Er ist nicht nur kostenlos, sondern lässt mich (fast) so schnell wie mit Sublime Text 3 arbeiten, während er gleichzeitig viele Annehmlichkeiten einer echten IDE mitbringt.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Der%20Umstieg%20auf%20Microsoft%20Visual%20Studio%20Code&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2018-09-19-umstieg-auf-microsoft-visual-studio-code%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/2018-09-19-umstieg-auf-microsoft-visual-studio-code/",
        "pubDate": "Wed, 19 Sep 2018 19:42:47 +0200",
        "atom_published": "2018-09-19T19:42:47+02:00",
        "atom_updated": "2020-02-27T10:17:00+01:00",
        "guid": "user/posts/2018-09-19-umstieg-auf-microsoft-visual-studio-code/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "Git",
          "PHP",
          "Programmierung",
          "Review",
          "Webdevelop"
        ]
      },
      {
        "title": "PHP in Javascript übersetzen",
        "description": "<p>Schon vor längerer Zeit hatte ich mir darüber Gedanken gemacht, <a href=\"http://blog.3960.org/post/18447181636/php-in-javascript-%C3%BCbersetzen\">wie man PHP-Klassen in Javascript-Objekte übersetzt</a>. Jetzt habe ich einen kleinen Helfer bauen können, der einen Großteil der Arbeit automatisiert.</p>",
        "content_encoded": "<p>Schon vor längerer Zeit hatte ich mir darüber Gedanken gemacht, <a href=\"http://blog.3960.org/post/18447181636/php-in-javascript-%C3%BCbersetzen\">wie man PHP-Klassen in Javascript-Objekte übersetzt</a>. Jetzt habe ich einen kleinen Helfer bauen können, der einen Großteil der Arbeit automatisiert.</p>\n<!-- more -->\n<p id=\"more\">Wie man am Beispiel eines kleinen <a href=\"https://3960.org/sandbox/vigenere.php\">Vigenère-Listings</a> sieht, sind die Strukturen von PHP-Klassen und Javascript-Objekten eigentlich sehr gleichartig. Mit der folgenden Javascript kann man einen Großteil der Transformation durchführen, und muss danach nur noch einige Details anpassen.</p>\n<pre><code class=\"language-javascript\"><u>/**\n * Afterwards you will need to:\n * - add `var` in front of uninitialized variables\n * - move default values for function parameters into function body like `param = param || 'default';`\n * - add `,` after every object property (variables &amp; methods)\n * - replace PHP functions\n * @param  {String} php [description]\n * @return {String}     [description]\n */</u>\n<b>var</b> transformPhpToJs = <b>function</b>(php) {\n  <i>return</i> php\n    .<b>replace</b>(/&lt;<samp>\\?</samp>php<samp>\\s</samp>*/g, <kbd>''</kbd>)\n    .<b>replace</b>(/<samp>\\?</samp>&gt;<samp>\\s</samp>*/g, <kbd>''</kbd>)\n    .<b>replace</b>(/(<b>protected</b>|<b>public</b>|<b>private</b>|<b>const</b>) (<samp>\\S</samp>+)<samp>\\s</samp>?=/g, <kbd>'$2 :'</kbd>)\n    .<b>replace</b>(/(<b>protected</b>|<b>public</b>|<b>private</b>|<b>const</b>) /g, <kbd>''</kbd>)\n    .<b>replace</b>(/(<b>function</b>) (<samp>\\S</samp>+)<samp>\\s</samp>?/g, <kbd>'$2: $1'</kbd>)\n    .<b>replace</b>(/(<b>class</b>) (<samp>\\S</samp>+)<samp>\\s</samp>?/g, <kbd>'var $2 = '</kbd>)\n    .<b>replace</b>(/<samp>\\$</samp>/g, <kbd>''</kbd>)\n    .<b>replace</b>(/SELF::/g, <kbd>'this.'</kbd>)\n    .<b>replace</b>(/-&gt;/g, <kbd>'.'</kbd>)\n    .<b>replace</b>(/<b>array</b><samp>\\(</samp>([<samp>\\s</samp><samp>\\S</samp>]*?)<samp>\\)</samp>;/g, <kbd>'[$1];'</kbd>)\n    .<b>replace</b>(/<samp>=&gt;</samp>/g, <kbd>':'</kbd>)\n    .<b>replace</b>(/empty<samp>\\(</samp>(.+?)<samp>\\)</samp>/g, <kbd>'!$1'</kbd>)\n    .<b>replace</b>(/preg_replace<samp>\\(</samp>[<kbd>'&quot;](.+?)['</kbd>&quot;],(.+?),<samp>\\s</samp>?(.+?)<samp>\\s</samp>?<samp>\\)</samp>/g, <kbd>'$3.<b>replace</b>(/$1/g,$2)'</kbd>)\n    .<b>replace</b>(/preg_match<samp>\\(</samp>[<kbd>'&quot;](.+?)['</kbd>&quot;],<samp>\\s</samp>?(.+?)<samp>\\s</samp>?<samp>\\)</samp>/g, <kbd>'$2.<b>match</b>(/$1/)'</kbd>)\n  ;\n}\n</code></pre>\n<p>Diese Funktion könnt ihr z.B. in die <code>F12</code>-Konsole von eurem Browser eingeben, und danach euren PHP-Quelltext <em>im Browser</em> übersetzen lassen. Folgender Aufruf gibt euch das transformierte PHP zurück:</p>\n<pre><code class=\"language-javascript\">transformPhpToJs(<kbd>`INSERT YOUR PHP HERE`</kbd>);\n</code></pre>\n<p>Geht dafür wie folgt vor:</p>\n<ol>\n<li>Öffnet in Google Chrome oder Mozilla Firefox mit <code>F12</code> eure Entwicklerkonsole.</li>\n<li>Wechselt auf den Tab „Konsole“.</li>\n<li>Kopiert o.a. Funktion in die Konsole und drückt <code>Enter</code>.</li>\n<li>Jetzt könnt ihr mit dem o.a. Aufruf euren dort eingefügten PHP-Code in Javascript übersetzen lassen.</li>\n<li>Die Ausgabe könnt ihr nun kopieren und in eine Datei einfügen, um danach die letzten Anpassungen vorzunehmen.</li>\n</ol>\n<p>Natürlich muss das Ergebnis noch nachbearbeitet werden, aber für die meisten Fälle erledigt es zumindest die groben Arbeiten. Danach könnt ihr z.B. daran gehen <a href=\"https://journal.3960.org/posts/node-js-pattern-modul/\">die richtige NodeJS-Modul-Struktur einzubauen</a>.</p>\n<p><strong>Update:</strong> <a href=\"https://github.com/trendfischer\">Mathias</a> hat mich auf <a href=\"http://locutus.io/php/\">Locutus</a> aufmerksam gemacht:</p>\n<blockquote><p>Locutus is a project that seeks to assimilate other languages’ standard libraries to JavaScript.</p></blockquote>\n<p>Hier findet ihr viele PHP-Funktionen als Javascript-Module umgesetzt. Die ganze Bibliothek ist zwar mit Augenzwinkern zu verstehen, für schnelle Erfolgserlebnisse eignet sie sich aber alle Mal.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=PHP%20in%20Javascript%20%C3%BCbersetzen&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2F2017-08-18-php-javascript-uebersetzen%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/2017-08-18-php-javascript-uebersetzen/",
        "pubDate": "Fri, 18 Aug 2017 19:02:00 +0200",
        "atom_published": "2017-08-18T19:02:00+02:00",
        "atom_updated": "2020-01-31T16:55:40+01:00",
        "guid": "user/posts/2017-08-18-php-javascript-uebersetzen.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Javascript",
          "Programmierung",
          "NodeJS",
          "Für Tumblr"
        ]
      },
      {
        "title": "Der kleinste PHP-Geocoder der Welt",
        "description": "<p>Mittels eines kleinen Dreizeilers und der Hilfe von <a href=\"https://www.openstreetmap.de/\">OpenStreetMap</a> kann man mit PHP Adressen in WGS84-Geokoordinaten verwandeln:</p>\n<pre><code class=\"language-php\"><u>/**\n * Convert address into WGS84 geo coordinates.\n * @see http://wiki.openstreetmap.org/wiki/Nominatim\n * @param string $address\n * @return array with lat / lon\n */</u>\n<b>function</b> getCoordinates(<b>string</b> <var>$address</var>): <b>array</b>\n{\n    <var>$url</var> = <kbd>'https://nominatim.openstreetmap.org/search?q='</kbd> . urlencode(<var>$address</var>) . <kbd>'&amp;format=json&amp;limit=1'</kbd>;\n    <var>$json</var> = json_decode(file_get_contents(<var>$url</var>), <samp>true</samp>);\n\n    <i>return</i> (!empty(<var>$json</var>[<em>0</em>])) ? <var>$json</var>[<em>0</em>] : [<kbd>'lat'</kbd> =&gt; <samp>null</samp>, <kbd>'lon'</kbd> =&gt; <samp>null</samp>];\n}\n</code></pre>\n<p>Diese Lösung ignoriert natürlich ein paar Komplikationen, die beim Anfragen der Schnittstelle auftreten können. Für eine einfache Anfrage mit geringer Frequenz und ggf. auch längerer Antwortzeit liefert die o.a. Funktion ein ganz passables Ergebnis.</p>",
        "content_encoded": "<p>Mittels eines kleinen Dreizeilers und der Hilfe von <a href=\"https://www.openstreetmap.de/\">OpenStreetMap</a> kann man mit PHP Adressen in WGS84-Geokoordinaten verwandeln:</p>\n<pre><code class=\"language-php\"><u>/**\n * Convert address into WGS84 geo coordinates.\n * @see http://wiki.openstreetmap.org/wiki/Nominatim\n * @param string $address\n * @return array with lat / lon\n */</u>\n<b>function</b> getCoordinates(<b>string</b> <var>$address</var>): <b>array</b>\n{\n    <var>$url</var> = <kbd>'https://nominatim.openstreetmap.org/search?q='</kbd> . urlencode(<var>$address</var>) . <kbd>'&amp;format=json&amp;limit=1'</kbd>;\n    <var>$json</var> = json_decode(file_get_contents(<var>$url</var>), <samp>true</samp>);\n\n    <i>return</i> (!empty(<var>$json</var>[<em>0</em>])) ? <var>$json</var>[<em>0</em>] : [<kbd>'lat'</kbd> =&gt; <samp>null</samp>, <kbd>'lon'</kbd> =&gt; <samp>null</samp>];\n}\n</code></pre>\n<p>Diese Lösung ignoriert natürlich ein paar Komplikationen, die beim Anfragen der Schnittstelle auftreten können. Für eine einfache Anfrage mit geringer Frequenz und ggf. auch längerer Antwortzeit liefert die o.a. Funktion ein ganz passables Ergebnis.</p>\n<!-- more -->\n<h2 id=\"more\">Komplizierter geht immer</h2>\n<p>Nach einem Refactoring mit <a href=\"https://www.php.net/manual/en/book.curl.php\">cURL</a> sieht das bereits deutlich komplexer aus, kann aber auch auf deutlich mehr Fehlerfälle reagieren und bricht bei Timeouts auch ab:</p>\n<pre><code class=\"language-php\"><b>function</b> getCoordinates(<b>string</b> <var>$address</var>): <b>array</b>\n{\n    <var>$url</var> = <kbd>'https://nominatim.openstreetmap.org/search?q='</kbd> . urlencode(<var>$address</var>) . <kbd>'&amp;format=json&amp;limit=1'</kbd>;\n    <var>$ch</var> = curl_init();\n    curl_setopt(<var>$ch</var>, CURLOPT_URL, <var>$url</var>);\n    curl_setopt(<var>$ch</var>, CURLOPT_HEADER, <em>0</em>);\n    curl_setopt(<var>$ch</var>, CURLOPT_RETURNTRANSFER, <em>1</em>);\n    curl_setopt(<var>$ch</var>, CURLOPT_TIMEOUT, <em>3</em>);\n    curl_setopt(<var>$ch</var>, CURLOPT_USERAGENT, <kbd>'Some PHP Function'</kbd>);\n    curl_setopt(<var>$ch</var>, CURLOPT_HTTPHEADER, [<kbd>'Accept: application/json'</kbd>]);\n    <var>$response</var> = curl_exec(<var>$ch</var>);\n    <var>$httpCode</var> = curl_getinfo(<var>$ch</var>, CURLINFO_HTTP_CODE);\n    curl_close(<var>$ch</var>);\n\n    <i>if</i> (<var>$httpCode</var> &lt; <em>300</em> &amp;&amp; <var>$response</var>) {\n        <var>$json</var> = json_decode(<var>$response</var>, <samp>true</samp>);\n        <i>if</i> (<var>$json</var>[<em>0</em>]) {\n            <i>return</i> <var>$json</var>[<em>0</em>];\n        }\n    }\n\n    <i>return</i> [<kbd>'lat'</kbd> =&gt; <samp>null</samp>, <kbd>'lon'</kbd> =&gt; <samp>null</samp>];\n}\n</code></pre>\n<p>Die API ist aber nicht für den Einsatz für Bulk-Adressumwandlung geeignet – wie auch die <a href=\"https://operations.osmfoundation.org/policies/nominatim/\">Nutzungsbedingung der API</a> erläutern.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Der%20kleinste%20PHP-Geocoder%20der%20Welt&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2Fkleinste-php-geocoder-welt%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/kleinste-php-geocoder-welt/",
        "pubDate": "Mon, 20 Mar 2017 19:46:10 +0100",
        "atom_published": "2017-03-20T19:46:10+01:00",
        "atom_updated": "2020-01-31T16:54:00+01:00",
        "guid": "user/posts/kleinste-php-geocoder-welt.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Geografie",
          "Webdevelop"
        ]
      },
      {
        "title": "Der einfachste PHP-Objekt-Debugger für den Browser",
        "description": "<p>Die fantastische <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Console/log\">Javascript-<code>console</code></a> kann ganz wunderbar strukturierte Javascript-Informationen ausgeben. Mit einem relativ einfachen Trick kann sie auch strukturierte PHP-Daten (wie z.B. Arrays und Objekte) ausgeben.</p>",
        "content_encoded": "<p>Die fantastische <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Console/log\">Javascript-<code>console</code></a> kann ganz wunderbar strukturierte Javascript-Informationen ausgeben. Mit einem relativ einfachen Trick kann sie auch strukturierte PHP-Daten (wie z.B. Arrays und Objekte) ausgeben.</p>\n<!-- more -->\n<p id=\"more\">Folgende PHP-Funktion kürzt diesen Prozess deutlich ab:</p>\n<pre><code class=\"language-php\"><u>/**\n * Debug PHP variable to browser console\n * @param  mixed $mixed [description]\n */</u>\n<b>function</b> console_log(<var>$mixed</var>)\n{\n    <i>echo</i>(<kbd>'&lt;script&gt;console.log('</kbd> . json_encode(<var>$mixed</var>) . <kbd>');&lt;/script&gt;'</kbd>);\n}\n</code></pre>\n<p>Die Funktion erzeugt einfach das notwendige HTML, um den Inhalt des Parameters an die Console eines Browsers zu übergeben. Die strukturierten Daten sieht man dann beim nächsten Neuladen der betreffenden Seite. (Die Console öffnet man bei den meisten Browsern übrigens via <code>F12</code> bzw. <code>Ctrl+Shift+C</code>.)</p>\n<p>In PhpStorm kann man auch ein <a href=\"https://www.jetbrains.com/help/phpstorm/2016.2/creating-and-editing-live-templates.html\">Live-Template definieren</a>, und dann mittels <code>Ctrl+J</code> / <code>Ctrl+Alt+T</code> aufrufen:</p>\n<pre><code class=\"language-php\"><i>echo</i>(<kbd>'&lt;script&gt;console.log('</kbd> . json_encode(<var>$SELECTION</var>$) . <kbd>');&lt;/script&gt;'</kbd>);\n</code></pre>\n<h3 id=\"beispiel\">Beispiel</h3>\n<p>So erzeugt der folgende Code…</p>\n<pre><code class=\"language-php\"><var>$coords</var> = Coordinates::set(<em>40.7143528</em>, <em>-74.0059731</em>, <em>10</em>, <kbd>'New York City'</kbd>);\n<var>$coords2</var> = <var>$coords</var>-&gt;getRelativeCoordinates(<em>1000</em>, <em>45</em>);\n<i>echo</i>(<kbd>'&lt;script&gt;console.log('</kbd> . json_encode(<var>$coords2</var>) . <kbd>');&lt;/script&gt;'</kbd>);\n</code></pre>\n<p>…die folgende Ausgabe im Browser:</p>\n<p><img src=\"https://journal.3960.org/posts/einfachste-php-objekt-debugger-fuer-browser/php-console.png\" width=\"403\" height=\"106\" style=\"--aspect-ratio: 403/106;\" alt=\"Eine Console-Ausgabe im Browser-Inspector\" /></p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Der%20einfachste%20PHP-Objekt-Debugger%20f%C3%BCr%20den%20Browser&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2Feinfachste-php-objekt-debugger-fuer-browser%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/einfachste-php-objekt-debugger-fuer-browser/",
        "pubDate": "Thu, 22 Dec 2016 19:33:16 +0100",
        "atom_published": "2016-12-22T19:33:16+01:00",
        "atom_updated": "2020-09-21T12:22:07+02:00",
        "guid": "user/posts/einfachste-php-objekt-debugger-fuer-browser/index.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Für Tumblr"
        ]
      },
      {
        "title": "Der kleinste PHP-Autoloader der Welt",
        "description": "<p>Autoloading ist in PHP eine feine Sache. Statt jede einzelne Klasse mittels eigenem <code>require</code> einzubinden, kann man bei existierendem Autoloader einfach durch Aufruf der Klasse diese laden.</p>",
        "content_encoded": "<p>Autoloading ist in PHP eine feine Sache. Statt jede einzelne Klasse mittels eigenem <code>require</code> einzubinden, kann man bei existierendem Autoloader einfach durch Aufruf der Klasse diese laden.</p>\n<!-- more -->\n<p id=\"more\">Eine Klasse legt man dabei einfach in einer Verzeichnisstruktur ab, z.B. unter <code>Example/Foo/Bar.php</code>:</p>\n<pre><code class=\"language-php\"><u>// Example/Foo/Bar.php</u>\n\n<b>namespace</b> Example<samp>\\F</samp>oo;\n\n<b>class</b> Bar {\n    <u>// Stuff here</u>\n}\n</code></pre>\n<p>Der kleinste Autoloader der Welt mit <a href=\"https://www.php.net/manual/en/function.spl-autoload-register.php\"><code>autoload_register</code></a> verarbeitet dann alle in PHP ansonsten unbekannten Klassen:</p>\n<pre><code class=\"language-php\">spl_autoload_register(<b>function</b>(<b>string</b> <var>$classname</var>) {\n    <i>require</i>(__DIR__ . <kbd>'/'</kbd> . str_replace(<kbd>'\\\\'</kbd>, <kbd>'/'</kbd>, <var>$classname</var>) . <kbd>'.php'</kbd>);\n});\n</code></pre>\n<p>Dabei muss der vordere Part mit <code>__DIR__ . ‚/‘</code> natürlich auf den richtigen Basispfad gesetzt werden, in dem dann die Klassen liegen.</p>\n<p>Und die Klasse setzt man dann nach eingeschalteten Autoloader wie folgt ein:</p>\n<pre><code class=\"language-php\"><u>// See, no require here</u>\n\n<var>$bar</var> = <i>new</i> <samp>\\E</samp>xample<samp>\\F</samp>oo<samp>\\B</samp>ar();\n</code></pre>\n<p>Den kleinsten PHP-Autoloader der Welt kann man auch gut mit anderen Autoloadern zusammen verwenden, wie z.B. dem Composer-Autoloader. </p>\n<p><strong>Update 2022–10</strong>: Seit PHP8 spätestens ist die Verwendung von <a href=\"https://www.php.net/manual/en/function.autoload.php\"><code>__autload</code></a> nicht mehr möglich, daher benutzt das Beispiel nur noch <a href=\"https://www.php.net/manual/en/function.spl-autoload-register.php\"><code>autoload_register</code></a>.</p><img src=\"https://stats.3960.org/p.php?idsite=2amp;action_name=Der%20kleinste%20PHP-Autoloader%20der%20Welt&amp;url=https%3A%2F%2Fjournal.3960.org%2Fposts%2Fkleinste-php-autoloader-welt%2F%3Futm_source%3Dnewsfeed_view\" style=\"border:0;\" alt=\"\" />",
        "link": "https://journal.3960.org/posts/kleinste-php-autoloader-welt/",
        "pubDate": "Fri, 09 Dec 2016 19:47:08 +0100",
        "atom_published": "2016-12-09T19:47:08+01:00",
        "atom_updated": "2022-10-30T15:00:09+01:00",
        "guid": "user/posts/kleinste-php-autoloader-welt.md",
        "author": "info@3960.org (Frank Boës)",
        "categories": [
          "PHP",
          "Programmierung",
          "Webdevelop"
        ]
      }
    ]
  }
}