<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Lars Moelleken on Medium]]></title>
        <description><![CDATA[Stories by Lars Moelleken on Medium]]></description>
        <link>https://medium.com/@voku?source=rss-5d6a4e584b43------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*2HZ5CVGMMlDutDOnmMlgPA.jpeg</url>
            <title>Stories by Lars Moelleken on Medium</title>
            <link>https://medium.com/@voku?source=rss-5d6a4e584b43------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 11:40:26 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@voku/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Generics in PHP via PHPDocs]]></title>
            <link>https://medium.com/@voku/generics-in-php-via-phpdocs-b987c5d6efab?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/b987c5d6efab</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Sat, 29 Aug 2020 00:34:02 GMT</pubDate>
            <atom:updated>2020-09-02T15:45:09.149Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*1z1g_hEHBFQRMDAG.jpg" /></figure><p>first published here: <a href="https://suckup.de/2020/08/generics-in-php-via-phpdocs/">https://suckup.de/2020/08/generics-in-php-via-phpdocs/</a></p><p>If you did not know that you can use Generics in PHP or you do not exactly know how to use it or why you should use it, then the next examples are for you.</p><h3>Type variables via @template</h3><p>The @template tag allows classes and functions to declare a generic type parameter. The next examples starts with simple functions, so that we understand how it works, and then we will see the power of this in classes.</p><h3><strong>A dummy function that will return the input.</strong></h3><p><strong>⇾</strong> <a href="https://phpstan.org/r/1922279b-9786-4523-939d-dddcfd4ebb86"><strong>https://phpstan.org/r/1922279b-9786-4523-939d-dddcfd4ebb86</strong></a></p><pre>&lt;?php    <br><br>    /**<br>     * @param \Exception $param<br>     * @return \Exception<br>     *<br>     * @template T of \Exception<br>     * @psalm-param T $param<br>     * @psalm-return T<br>     */<br>    function foo($param) { ... }<br><br>    foo(new \InvalidArgumentException()); // The static-analysis-tool knows that <br>                                          // the type is still &quot;\InvalidArgumentException&quot; <br>                                          // because of the type variable.</pre><p><strong>@template T of \Exception</strong> // here we create a new type variable, and we force that it must be an instance of \Exception</p><p><strong>@phpstan-param T $param</strong> // here we say that the static-analysis-tool need to remember the type that this variable had before (you can use @psalm-* or @phpstan-* both works with both tools)</p><p><strong>@phpstan-return T</strong> // and that the return type is the same as the input type</p><h3><strong>A simple function that gets the first element of an array or a fallback.</strong></h3><p>In the @param PHPDocs we write “mixed” because this function can handle different types. But this information is not very helpful if you want to understand programmatically what the code does, so we need to give the static-analysis-tools some more information.</p><p><strong>⇾ </strong><a href="https://phpstan.org/r/1900a2af-f5c1-4942-939c-409928a5ac4a"><strong>https://phpstan.org/r/1900a2af-f5c1-4942-939c-409928a5ac4a</strong></a></p><pre>&lt;?php<br><br>    /**<br>     * @param array&lt;mixed&gt; $array<br>     * @param mixed        $fallback &lt;p&gt;This fallback will be used, if the array is empty.&lt;/p&gt;<br>     *<br>     * @return mixed|null<br>     *<br>     * @template TFirst<br>     * @template TFirstFallback<br>     * @psalm-param TFirst[] $array<br>     * @psalm-param TFirstFallback $fallback<br>     * @psalm-return TFirst|TFirstFallback<br>     */<br>    function array_first(array $array, $fallback)<br>    {<br>        $key_first = array_key_first($array);<br>        if ($key_first === null) {<br>            return $fallback;<br>        }<br><br>        return $array[$key_first];<br>    }<br><br>    array_first([1, 2, 3], null); </pre><pre>    if ($a === &#39;foo&#39;) { // The static-analysis-tool knows that <br>                        // === between int|null and &#39;foo&#39; will always evaluate to false.<br>        // ...<br>    }</pre><p><strong>@template TFirst</strong> // we again define your typed variables</p><p><strong>@template TFirstFallback</strong> // and one more because we have two inputs where we want to keep track of the types</p><p><strong>@psalm-param TFirst[] $array</strong> // here we define that $array is an array of TFirst types</p><p><strong>@psalm-param TFirstFallback $fallback</strong> // and that $fallback is some other type that comes into this function</p><p><strong>@psalm-return TFirst|TFirstFallback</strong> // now we define the return type as an element of the $array or the $fallback type</p><h3><strong>Very basic <em>Active Record</em> + Generics</strong></h3><p>The IDE support for generics is currently not there, :-/ so that we still need some hacks (see @method) for e.g. PhpStorm to have autocompletion.</p><p><a href="https://github.com/voku/phpstorm_issue_53352/tree/master/src/Framework/ActiveRecord"><strong>https://github.com/voku/phpstorm_issue_53352/tree/master/src/Framework/ActiveRecord</strong></a></p><p><a href="https://phpstan.org/r/f88f5cd4-1bb9-4a09-baae-069fddb10b12"><strong>https://phpstan.org/r/f88f5cd4-1bb9-4a09-baae-069fddb10b12</strong></a></p><h3><strong>A more complex collection example.</strong></h3><p><a href="https://github.com/voku/Arrayy/tree/master/src/Type"><strong>https://github.com/voku/Arrayy/tree/master/src/Type</strong></a></p><p>In the end we can extend the “AbstractCollection” and the static-analysis-tools knows the types of all the methods.</p><p><strong>Links:</strong></p><p>⇾ <a href="https://phpstan.org/blog/generics-in-php-using-phpdocs">https://phpstan.org/blog/generics-in-php-using-phpdocs</a></p><p>⇾ <a href="https://psalm.dev/docs/annotating_code/templated_annotations/">https://psalm.dev/docs/annotating_code/templated_annotations/</a></p><p>⇾ <a href="https://stitcher.io/blog/php-generics-and-why-we-need-them">https://stitcher.io/blog/php-generics-and-why-we-need-them</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b987c5d6efab" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[❤️ Simple PHP Code Parser]]></title>
            <link>https://medium.com/@voku/%EF%B8%8F-simple-php-code-parser-9cdd325fbd39?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/9cdd325fbd39</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Fri, 07 Aug 2020 00:19:19 GMT</pubDate>
            <atom:updated>2020-08-07T00:29:00.801Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*sU5_pwHkmM9nGnMy.jpg" /></figure><p><em>first published here:</em> <a href="https://suckup.de/2020/08/simple-php-code-parser/">https://suckup.de/2020/08/simple-php-code-parser/</a></p><p>It based on code from “ <a href="https://github.com/JetBrains/phpstorm-stubs">JetBrains/phpstorm-stubs</a> “ but instead of Php-Reflection we now use nikic/PHP-Parser, BetterReflection, phpDocumentor and PHPStan/phpdoc-parser internally. So, you can get even more information about the code. For example, psalm- / phpstan-phpdoc annotations or inheritdoc from methods.</p><h3>Install:</h3><p>composer require voku/simple-php-code-parser</p><h3>Link:</h3><p><a href="https://github.com/voku/Simple-PHP-Code-Parser">voku/Simple-PHP-Code-Parser</a></p><h3>More:</h3><ul><li>there is also a “.phar” version for e.g. phpdoc checks: <a href="https://github.com/voku/Simple-PHP-Code-Parser/releases">voku/Simple-PHP-Code-Parser/releases</a></li><li>and an API documentations helper for your README file, based on the simple php code parser: <a href="https://github.com/voku/Php-Readme-Helper">voku/Php-Readme-Helper</a></li></ul><p><strong>Example</strong>: get value from define in “\foo\bar” namespace</p><pre>$code = &#39;<br>  &lt;?php<br>  namespace foo\bar;<br>  define(&quot;FOO_BAR&quot;, &quot;Lall&quot;);<br>&#39;;</pre><pre>$phpCode = PhpCodeParser::getFromString($code);<br>$phpConstants = $phpCode-&gt;getConstants();</pre><pre>$phpConstants[&#39;\foo\bar\FOO_BAR&#39;]-&gt;value; // &#39;Lall&#39;</pre><p><strong>Example</strong>: get information about @property phpdoc from a class</p><pre>$code = &#39;<br>  &lt;?php<br>  /** <br>   * @property int[] $foo <br>   */<br>  abstract class Foo { }<br>&#39;;</pre><pre>$phpCode = PhpCodeParser::getFromString($code);<br>$phpClass = $phpCode-&gt;getClass(&#39;Foo&#39;);</pre><pre>$phpClass-&gt;properties[&#39;foo&#39;]-&gt;typeFromPhpDoc); // int</pre><p><strong>Example</strong>: get classes from a string (or from a class-name or from a file or from a directory)</p><pre>$code = &#39;<br>&lt;?php<br>namespace voku\tests;<br>class SimpleClass {}<br>$obja = new class() {};<br>$objb = new class {};<br>class AnotherClass {}<br>&#39;;</pre><pre>$phpCode = \voku\SimplePhpParser\Parsers\PhpCodeParser::getFromString($code);<br>$phpClasses = $phpCode-&gt;getClasses();</pre><pre>var_dump($phpClasses[&#39;voku\tests\SimpleClass&#39;]); // &quot;PHPClass&quot;-object</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9cdd325fbd39" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Arrayy: A Quick Overview of map(), filter(), and reduce()]]></title>
            <link>https://medium.com/@voku/arrayy-a-quick-overview-of-map-filter-and-reduce-264ddf90e6c6?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/264ddf90e6c6</guid>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[php]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Tue, 28 Jul 2020 22:48:16 GMT</pubDate>
            <atom:updated>2020-07-29T06:22:18.738Z</atom:updated>
            <content:encoded><![CDATA[<p>first published here: <a href="https://suckup.de/2020/07/arrayy-a-quick-overview-of-map-filter-and-reduce/">http://suckup.de/2020/07/how-to-write-readable-code/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*v3sYrOyVoXejcgKW.jpeg" /></figure><p><strong>Arrayy: A PHP array manipulation library. Compatible with PHP 7+</strong></p><p>The next examples are using the php array manipulation library “Arrayy” which is using <a href="https://www.php.net/manual/en/language.generators.overview.php">generators</a> internally for many operations.</p><p>⇾ <a href="https://github.com/voku/Arrayy">https://github.com/voku/Arrayy</a></p><p>StringCollection::create([&#39;Array&#39;, &#39;Array&#39;])-&gt;unique()-&gt;append(&#39;y&#39;)-&gt;implode(); // Arrayy</p><p><strong>map</strong>: transform all values in the collection</p><pre>StringCollection::create([&#39;foo&#39;, &#39;Foo&#39;])-&gt;map(&#39;mb_strtoupper&#39;); // StringCollection[&#39;FOO&#39;, &#39;FOO&#39;]</pre><p><strong>filter</strong>: pass all values to the truth test</p><pre>$closure = function ($value) { return $value % 2 !== 0; } IntCollection::create([1, 2, 3, 4])-&gt;filter($closure); // IntCollection[0 =&gt; 1, 2 =&gt; 3]</pre><p><strong>reduce</strong>: transform all values into a new result</p><pre>IntCollection::create([1, 2, 3, 4])-&gt;reduce( function ($carry, $item) { return $carry * $item; }, 1 ); // IntCollection[24]</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=264ddf90e6c6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to write readable code?]]></title>
            <link>https://medium.com/@voku/how-to-write-readable-code-f1f453b13881?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/f1f453b13881</guid>
            <category><![CDATA[clean-code]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Thu, 23 Jul 2020 23:20:23 GMT</pubDate>
            <atom:updated>2020-07-29T06:21:11.500Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VgFgZc7ohU1zcV3j.jpg" /></figure><p>first published here: <a href="http://suckup.de/2020/07/how-to-write-readable-code/">http://suckup.de/2020/07/how-to-write-readable-code/</a></p><h3>Why do we write unreadable code?</h3><p>I think we are mostly lazy, and so we use names from underlying structures like APIs or the databases, instead of using names that describe the current situation.</p><p>For example, the method “ Cart-&gt;getUser(): User&quot;: There is a &quot;user_id&quot; in the underlying database table, so the developer chooses the name &quot;getUser()&quot; where the User will be returned. The problem is &quot;getX&quot; and &quot;setX&quot; are terrible names for methods because they did not say you anything about the context of these methods. Maybe something like &quot; Cart-&gt;orderUser(): OrderUser&quot; and at some point &quot; Cart-&gt;orderApprovalUser(): OrderApprovalUser &quot; are better names. But keep in mind that this depends on your use cases. And please rename also e.g. your &quot;user_id&quot; in the cart database table, so that you keep the code consistent and readable.</p><p>In Eric Evans’ book <a href="http://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215">Domain-Driven Design</a> he introduces the concept of a “ <a href="http://martinfowler.com/bliki/UbiquitousLanguage.html">Ubiquitous Language</a>” — a shared, common vocabulary that the entire team shares when discussing or writing software. This “entire team” is made up of designers, developers, the product owner and any other domain experts that exist at the organization. And take the note that it is important that your team have a <strong>domain expert! </strong>Mostly we are not the expert in the field we are writing software for, so we need some professional input. If you build an invoice-system you will need an expert in finance-questions and -laws.</p><h3>if ()</h3><pre># BAD: <br>if ($userExists == 1) { } </pre><pre># BETTER: use &quot;===&quot; <br>if ($userExists === 1) {} </pre><pre># BETTER: use the correct type<br>if ($userExists === true) {} </pre><pre># BETTER: use something readable <br>if ($this-&gt;userExists()) {}<br></pre><pre># BAD: <br>$foo = 3; <br>if ($lall === 2) { $foo = 2; } </pre><pre># BETTER: use if-else <br>if ($lall === 2) { <br>   $foo = 2; <br>} else { <br>   $foo = 3;<br>} </pre><pre># BETTER: only for simple if-else <br>$foo = ($lall === 2) ? 2 : 3; </pre><pre># BETTER: use something readable <br>$foo = $this-&gt;foo($lall);</pre><h3>… more examples in the original post:</h3><p><a href="http://suckup.de/2020/07/how-to-write-readable-code/">http://suckup.de/2020/07/how-to-write-readable-code/</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f1f453b13881" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Modern PHPDoc Annotations]]></title>
            <link>https://medium.com/@voku/modern-phpdoc-annotations-cdb25dba2813?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/cdb25dba2813</guid>
            <category><![CDATA[code]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[phpstorm]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Tue, 04 Feb 2020 23:17:45 GMT</pubDate>
            <atom:updated>2020-02-04T23:29:39.383Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/480/0*YlMuipIfdZIa8hao.png" /></figure><p>first published here: <a href="https://suckup.de/2020/02/modern-phpdoc-annotations/">https://suckup.de/2020/02/modern-phpdoc-annotations/</a></p><p>We will start very simple with PhpStorm and default PHPDoc, then we will increase the complexity step by step until we have auto-completion for array keys directly from the database with generics, immutable and type safety support.</p><h3>1.0 PhpStorm &amp; auto-generate PHPDoc blocks</h3><blockquote><em>„For documentation comments, PhpStorm provides completion that is enabled by default. PhpStorm creates stubs of „ </em><a href="http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_phpDocumentor.pkg.html"><em>PHPDoc blocks</em></a><em>” when you type the </em>/**<em> opening tag and press , or press and appoint the code construct (a class, a method, a function, and so on) to document. Depending on your choice, PhpStorm will create the required tags or add an empty documentation stub.”</em></blockquote><p><a href="https://www.jetbrains.com/help/phpstorm/phpdoc-comments.html">https://www.jetbrains.com/help/phpstorm/phpdoc-comments.html</a></p><h3>1.1 Return $this|static|self</h3><p>It’s quite annoying that php itself currently only have „self” as return type ( <a href="http://suckup.de/2016/01/php-arrays-zusammenzufuehren/">https://wiki.php.net/rfc/static_return_type</a>) for the current class. Because of „ <a href="https://www.php.net/manual/en/language.oop5.late-static-bindings.php">late static binding</a> “ you can use „static” in your code to refer to the class a method was actually called on, even if the method is inherited. But in PHPDoc you can already use:</p><ul><li>@return <strong>$this</strong>: if you really return $this (e.g. for <em>fluent interface</em>)</li><li>@return <strong>static</strong>: refer to the class a method was actually called on</li><li>@return <strong>self</strong>: refer to the class a method was written in</li></ul><p><strong>Code:</strong></p><pre><em>/**</em><br> <em>* @return static</em><br> <em>*/</em><br>abstract function getFoo(): self;</pre><h3>1.2 New (and not that new) Array Syntax</h3><p>PhpStorm and (PHPStan &amp; Psalm) are supporting some new (and some not that new) array syntax for PHPDoc types, but for now PhpStorm will not auto-generate this types.</p><p>Examples:</p><ul><li><strong>int[]</strong>: an array with only INT values — [1, 4, 6, 8, 9, …]</li><li><strong>array&lt;int, int&gt;</strong>: an array with only INT values — [4 =&gt; 1, 8 =&gt; 4, 12 =&gt; 6, …]</li><li><strong>string[]</strong>: an array with only STRING values — [„foo”, „bar”, …]</li><li><strong>array&lt;int, string&gt;</strong>: an array with only STRING values — [4 =&gt; „foo”, 8 =&gt; „bar”, …]</li><li><strong>Order[]</strong>: an array with only „Order”-Object values — [Order, Order, …]</li><li><strong>array&lt;int|string, Order&gt;</strong>: an array with INT or STRING as key and „Order”-Object values — [4 =&gt; Order, ‘foo’ =&gt; Order, …]</li><li><strong>array&lt;int|string, mixed&gt;</strong>: an array with INT or STRING as key and mixed as values — [1 =&gt; 1, 4 =&gt; „foo”, 6 =&gt; \stdClass, …]</li><li><strong>array&lt;int, array&lt;int, string&gt;&gt;</strong>: an array with INT as key and and an array (with INT as key and string as value) as values — [1 =&gt; [1 =&gt; „foo”], 4 =&gt; [1 =&gt; 4], …]</li><li><strong>array&lt;int, string[]&gt;</strong>: an array with INT as key and and an array (with INT as key and string as value) as values — [1 =&gt; [„foo”, „lall”], 4 =&gt; [„öäü”, „bar”], …]</li><li><strong>array{output: string, debug: string}</strong>: an array with the key „output” and „debug”, the values are STRING values — [‚output’ =&gt; ‚foo’, ‚debug’ =&gt; ‚bar’]</li><li><strong>array&lt;int, array{output: string, debug: string}&gt;</strong>: an array with the key „output” and „debug”, the values are STRING values — [1 =&gt; [‚output’ =&gt; ‚foo’, ‚debug’ =&gt; ‚bar’], 3 =&gt; [‚output’ =&gt; ‚foo’, ‚debug’ =&gt; ‚bar’], …]</li></ul><p>Examples (only @psalm-*): supported by PHPStan &amp; Psalm</p><p><strong>Live-Examples:</strong></p><p>- Psalm: <a href="https://psalm.dev/r/922d4ba5b1">https://psalm.dev/r/922d4ba5b1</a></p><p>- PHPStan: <a href="https://phpstan.org/r/ce657ef4-9f18-46a1-b21a-e51e3a0e6d2d">https://phpstan.org/r/ce657ef4-9f18-46a1-b21a-e51e3a0e6d2d</a></p><p><strong>Code:</strong></p><pre><em>/**</em><br> <em>* @param array&lt;int|string, mixed&gt; $row</em><br> <em>*</em><br> <em>* @return array&lt;int|string, mixed&gt;</em><br> <em>*/</em><br>abstract function formatRow(array $row): array;</pre><p><strong>PhpStorm support?:</strong> Sadly PhpStorm did not have good support for these types, so that you often have to add „@psalm-*” PHPDoc comments. For example PhpStorm will accept „array&lt;int, Order&gt;” but PhpStorm will not understand the PHPDoc, so that you need to add e.g. „@param Order[] $order” and „@psalm-param array&lt;int, Order&gt; $order”.</p><h3>1.3 Dynamic Autocompletion (+ data from your database) via deep-assoc-completion</h3><p>If you have a method e.g. „formatRow($row)” you can use „getFieldArray()[0]” (data from the database — you have to connection the IDE with your database and your queries need to be analyzable by PhpStorm (take a look at the next screenshot) and combine static data from „getHeaderFieldArray()”, so that you have auto-completion from different sources.</p><p><strong>Code:</strong></p><pre><em>/**</em><br> <em>* @param array&lt;int|string, mixed&gt; $row = $this-&gt;getFieldArray()[0] + $this-&gt;getHeaderFieldArray()</em><br> <em>*</em><br> <em>* @return array&lt;int|string, mixed&gt;</em><br> <em>*/</em><br>abstract function formatRow(array $row): array;</pre><p>more information + examples: <a href="https://github.com/klesun/deep-assoc-completion">https://github.com/klesun/deep-assoc-completion</a></p><h3>1.4 Immutability Check via Static Code Analyses (via psalm)</h3><p>And there is even more. :) You can add PHPDoc annotation that will check if you really use immutable classes or at least methods. Please read more here: <a href="https://psalm.dev/articles/immutability-and-beyond">https://psalm.dev/articles/immutability-and-beyond</a></p><p><strong>Code:</strong> <strong>Live-Example:</strong></p><p>- Psalm: <a href="https://psalm.dev/r/5bac0a9a07">https://psalm.dev/r/5bac0a9a07</a></p><h3>1.5 Generics in PHP via Static Code Analyses</h3><p>We can also use Generics via code annotations. PHPStan &amp; Psalm both support it, but Psalm’s support is more feature complete and both tools can use the „@psalm-”-syntax. Here comes some simple examples.</p><p><strong>array_last</strong>: Will return the last array element from the $array (type: ) or the $fallback (type: <em>TLastFallback</em>). We tell the function that the types comes from the input parameters and that the input is an array of or <em>TLastFallback</em> from the fallback.</p><p><strong>array_first</strong>: Will return the first array element from the $array (type: ) or the $fallback (type: <em>TFirstFallback</em>). We tell the function that the types comes from the input params and that the input is an array of or <em>TFirstFallback</em> from the fallback.</p><p>So we can define „Templates” and map input arguments on that types, this can be even more complex if you use it in a class context and you map the „Templates” on class properties. But the logic will be the same.</p><p>Here is a more complex example: <a href="https://github.com/voku/Arrayy/blob/master/src/Collection/CollectionInterface.php">https://github.com/voku/Arrayy/blob/master/src/Collection/CollectionInterface.php</a></p><p><strong>PhpStorm support?</strong>: Noop, sadly we need to hack this via „PHPSTORM_META”, so here is an example:</p><ul><li>override(\array_filter(0), type(0)); // suppose first parameter type is MyClass[] then return type of array_filter will be MyClass[]</li><li>override(\array_reduce(0), elementType(0)); // suppose first parameter type is MyClass[] then return type of array_reduce will be MyClass</li></ul><p><strong>Read more here:</strong></p><p>- <a href="https://blog.jetbrains.com/phpstorm/2019/02/new-phpstorm-meta-php-features/">https://blog.jetbrains.com/phpstorm/2019/02/new-phpstorm-meta-php-features/</a></p><p>- <a href="https://gist.github.com/voku/477e5c22a67c2d37ca42150d94e371ea">https://gist.github.com/voku/477e5c22a67c2d37ca42150d94e371ea</a></p><h3>2.0 Resume</h3><p>It’s not perfect, and type check and auto-completion only with PHPDoc is not really what I expected for the year 2020. But it’s working and I hope PhpStorm will bring more support for the new types annotations in the future.</p><p><strong>More Links:</strong></p><p>- <a href="https://docs.phpdoc.org/latest/guides/types.html">https://docs.phpdoc.org/latest/guides/types.html</a></p><p>- <a href="https://scrutinizer-ci.com/docs/tools/php/php-analyzer/guides/annotating_code">https://scrutinizer-ci.com/docs/tools/php/php-analyzer/guides/annotating_code</a></p><p>- <a href="https://github.com/klesun/deep-assoc-completion/blob/master/docs/deep-keys-overview.md">https://github.com/klesun/deep-assoc-completion/blob/master/docs/deep-keys-overview.md</a></p><p>- <a href="https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types">https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#appendix-a-types</a></p><p>- <a href="https://psalm.dev/docs/annotating_code/supported_annotations/">https://psalm.dev/docs/annotating_code/supported_annotations/</a></p><p><em>Originally published at </em><a href="https://suckup.de/2020/02/modern-phpdoc-annotations/"><em>https://suckup.de</em></a><em> on February 4, 2020.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cdb25dba2813" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Do Not Fear The White Space in your Code]]></title>
            <link>https://medium.com/@voku/do-not-fear-the-white-space-in-your-code-5e470516a74b?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/5e470516a74b</guid>
            <category><![CDATA[code]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Tue, 28 Jan 2020 22:32:00 GMT</pubDate>
            <atom:updated>2020-01-28T23:38:39.041Z</atom:updated>
            <content:encoded><![CDATA[<p>first published here: <a href="https://suckup.de/2020/01/do-not-fear-the-white-space-in-your-code/">https://suckup.de/2020/01/do-not-fear-the-white-space-in-your-code/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/0*mCx-i3ppq2dICl-g.jpg" /></figure><blockquote><em>“White space is to be regarded as an active element, not a passive background.”<br>- Jan Tschichold</em></blockquote><p>At least since Apple is using massive white space for their product pages, every web-designer knows that the visitors prefer simple over the complex designs. And I think we can use some of this design principles to make also our code more readable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/0*X4qbHwPISk_ALY6U.jpg" /></figure><h3>What is white space?</h3><p>White space is created by pressing the Return key [\n|\r|\r\n], Spacebar key [ ], or the Tab key [\t]. White space is essentially any bit of space in your code that is not taken up by „physical” characters.</p><pre>[\n] = LF (Line Feed) → Used as a new line character in Unix/Mac OS X</pre><pre>[\r] = CR (Carriage Return) → Used as a new line character in Mac OS before X </pre><pre>[\r\n] = CR + LF → Used as a new line character in Windows</pre><p><a href="https://stackoverflow.com/a/15433225/1155858">https://stackoverflow.com/a/15433225/1155858</a></p><p>White space is critical for organizing our code, because we tend to find some relations between “things” to instinctively find a meaning in the composition. Whatever you see, your mind will filter, add and remove the given input and because of that we should prepare the input. So that we can easily consume the code.</p><ul><li><em>Leading White Space </em>(at the start of a line): Leading white space (i.e., indentation) is addressed elsewhere. (Indent with i.e. 4 spaces is a good default.)</li><li><em>Internal White Space </em>(in the code itself): The code is more readable with some white space between the different operations in a single line.</li><li><em>Trailing White Space </em>(at the end of a line): This kind of white spaces are unnecessary and can complicate diffs.</li></ul><p><em>Horizontal alignment</em>: Alignment can aid readability, but it creates problems for future maintenance, if you do not automate your code style.</p><p>„White space also makes content more readable. A study (Lin, 2004) found that good use of white space between paragraphs and in the left and right margins — <strong>increases comprehension by almost 20%</strong>. Readers find it easier to focus on and process generously spaced content.”<em>[Lin, 2004] Evaluating older adults’ retention in hypertext perusal: impacts of presentation media as a function of text topology by Dyi-Yih Michael Lin in „Computers in Human Behavior”, Volume 20, Issue 4, July 2004, Pages 491–503</em></p><h3>Where do we use white space?</h3><p>We mostly add white space after a parameter, function, method, if-, else-, while-, and other calls so that it’s easier to identify where code that belongs together starts and ends. Unlike a book in which you read from top to bottom, we mostly jump into the code and only read parts of the code. It’s more like a newspaper where you read the headline ( i . e . method name) and then jump to the next article ( i . e . method) .</p><h3>Whitespace changes are tricky for “diff”</h3><p>You can use something like “git diff -w” …</p><ul><li>-ignore-space-at-eol: Ignore changes in whitespace at EOL.</li><li>-b, -ignore-space-change: Ignore changes in amount of whitespace. This ignores whitespace at line end, and considers all other sequences of one or more whitespace characters to be equivalent.</li><li>-w, -ignore-all-space: Ignore whitespace when comparing lines. This ignores differences even if one line has whitespace where the other line has none.</li><li>-ignore-blank-lines: Ignore changes whose lines are all blank.</li></ul><p>… but the problems is still there, that white space changes hurts readability from default diffs in i.e. gitlab / github / etc. so that you should try to commit white space changes in a separated commit.</p><h3>Be consistent!</h3><p>T he most important and the only things that everybody will agree on is: “ <strong>Be consistent [and try to automate this process, please]! </strong>“ F or example, d on’t mix tabs and spaces or playing different braces for the same type of code . If the team (or project ) didn’t use any code style at all, then it’s quite simple, take an existing coding standard (try to follow the conventions already in place for a language, if there are any) where automation is already implemented for and stick to it in the first run.</p><h3>Design principles for your code</h3><p><strong>Proximity</strong>: Things closer together will be seen as belonging together.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/697/0*xD051lRhEHSaqGJz.png" /></figure><p>Code: Put code together that belongs together.</p><p><em>not that bad:</em></p><pre>.example_1,<br>.example_2 { <br> background: url(https://medium.com/images/example.png) center center no-repeat, <br> url(https://medium.com/images/example-2.png) top left repeat, <br> url(https://medium.com/images/example-3.png) top right no-repeat;<br>}</pre><p>I also like to have a single blank line before ‚break’, ‚continue’, ‚declare’, ‚return’, ‚throw’, ‚try’, so that you see that this is some kind of important code. But also the readability of simple „if”-statements can be improved.</p><pre>if (($loggedInKunde-&gt;bestellungen_genehmigen || $loggedInKunde-&gt;manuelle_bestellgenehmigung) &amp;&amp; ($wareneingang != 5)) {</pre><p><em>not that bad:</em></p><pre>if (<br> (<br> $loggedInKunde-&gt;bestellungen_genehmigen<br> ||<br> $loggedInKunde-&gt;manuelle_bestellgenehmigung<br> )<br> &amp;&amp;<br> $wareneingang != 5<br>) {</pre><p>better:</p><pre>if ($this-&gt;useOrderApproval()) {</pre><p>And please do not mix different things like variable assinment and „if”-statments in one blob of code.</p><pre>if (null !== $token = $this-&gt;tokenStorage-&gt;getToken()) {</pre><p><em>not that bad:</em></p><pre>$token = $this-&gt;tokenStorage-&gt;getToken();<br>if ($token !== null) {</pre><pre>$token = $this-&gt;tokenStorage-&gt;getToken();<br>if ($token-&gt;exists()) {</pre><p><strong>Similarity</strong>: T hings with the same characteristics (shape, color, shading, orientation, …) will be seen as belonging together.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/697/0*w4fVL9J5h71iJIG4.png" /></figure><p>Code: Write code that do similar things in the same code style.</p><pre>$valueArray = Bar::searchSplitButKeepQuotes($value);<br>if (\count($valueArray) &lt;= 0) { return &#39;&#39;; }</pre><pre>$valueFoo = Foo::searchSplit($value);<br>if (\count($valueFoo) === 0) {<br> return &#39;&#39;;</pre><pre>}</pre><p><em>not that bad:</em></p><pre>$bars = Bar::searchSplitButKeepQuotes($value);<br>if (\count($bars) === 0) {<br> return &#39;&#39;;<br>}</pre><pre>$foos = Foo::searchSplit($value);<br>if (\count($foos) === 0) {<br> return &#39;&#39;;<br>}</pre><p><strong>Symmetry</strong>: Our mind tends to perceive objects by finding a starting point and it’s pleased when it can find uniformity structures. Sometimes our mind will see such structures also if they are not really there.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/281/0*ISXdClqejHHXQfHr.jpg" /></figure><p>Code: Try to use uniformity code structures.</p><p><em>bad:</em></p><pre>if (widget.IsRegular) {<br>    service.Process(widget);<br>} else {<br>    var result = widget.Calculation();<br>    widget.ApplyResult(result);<br>    service.ProcessSpecial(widget);<br>}</pre><p><em>not that bad:</em></p><pre>if (widget.IsRegular) {<br>    service.Process(widget);<br>} else {<br>    service.ProcessSpecial(widget);<br>}</pre><p>Links:</p><ul><li><a href="https://google.github.io/styleguide/">https://google.github.io/styleguide/</a></li><li><a href="https://www.smashingmagazine.com/2013/02/using-white-space-for-readability-in-html-and-css/">https://www.smashingmagazine.com/2013/02/using-white-space-for-readability-in-html-and-css/</a></li><li><a href="https://www.smashingmagazine.com/2009/09/10-useful-usability-findings-and-guidelines/">https://www.smashingmagazine.com/2009/09/10-useful-usability-findings-and-guidelines/</a></li><li><a href="https://odetocode.com/blogs/scott/archive/2011/02/07/the-value-of-symmetry.aspx">https://odetocode.com/blogs/scott/archive/2011/02/07/the-value-of-symmetry.aspx</a></li><li><a href="https://manifesto.co.uk/design-principles-gestalt-white-space-perception/">https://manifesto.co.uk/design-principles-gestalt-white-space-perception/</a></li></ul><p><em>Originally published at </em><a href="https://suckup.de/2020/01/do-not-fear-the-white-space-in-your-code/"><em>https://suckup.de</em></a><em> on January 28, 2020.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5e470516a74b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Microsoft + Github = ?]]></title>
            <link>https://medium.com/@voku/microsoft-github-e3b3f0da65c1?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/e3b3f0da65c1</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[development]]></category>
            <category><![CDATA[microsoft]]></category>
            <category><![CDATA[php]]></category>
            <category><![CDATA[github]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Wed, 06 Jun 2018 01:26:55 GMT</pubDate>
            <atom:updated>2018-06-06T01:26:55.594Z</atom:updated>
            <content:encoded><![CDATA[<p>I first thought something like that …</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*NA3uIekJg8HdH7V6Ki3FEA.jpeg" /></figure><blockquote>from <a href="https://twitter.com/nickmorrott/status/1003414736682258432">https://twitter.com/nickmorrott/status/1003414736682258432</a></blockquote><p>… but I will keep my open source work at <a href="https://github.com/voku/">https://github.com/voku/</a>, because it hasn’t really changed anything: Gitbub itself wasn’t open source and Microsoft is committed to open source now, more than ever… so we will see if it’s good || bad for us.</p><p>“Our industry is really wack. We trust some rando startup with all our source code that could disappear any time and a company known to have a track record of supporting things for decades buys them out and now you all worry? Who else would you have liked to buy them?” — <a href="https://twitter.com/kellabyte/status/1003488466028486656">https://twitter.com/kellabyte/status/1003488466028486656</a></p><p>(Microsoft != Open Source) === false</p><ul><li>It <a href="https://github.com/Microsoft/dotnet">open-sourced all of the .NET framework on GitHub</a></li><li>It build out rich, system-level Windows features to <a href="https://github.com/Microsoft/WSL">bring a rich Unix shell to the platform</a> to make development easier</li><li>It began and continues to maintain one of the <a href="https://code.visualstudio.com/">best open-source code editors</a> available for every platform out there</li><li>It became the <a href="https://www.zdnet.com/article/from-open-source-hater-to-no-1-fan-microsoft-now-tops-google-facebook-in-github-contributors/">largest contributor to open source on GitHub</a>.</li><li>Microsoft’s whole Windows 10 platform is now built on open-source Progressive Web App technology</li></ul><p>from <a href="https://medium.com/@ow/microsoft-acquiring-github-is-a-good-thing-heres-why-6a6a57eb83ac">https://medium.com/@ow/microsoft-acquiring-github-is-a-good-thing-heres-why-6a6a57eb83ac</a></p><p>“Just so that I’m clear, I’m not declaring doom and gloom regarding the MS acquisition of github. It has every potential to be a good thing. It also has every potential to be a bad thing.” — <a href="https://twitter.com/SaraMG/status/1003654926847922176">https://twitter.com/SaraMG/status/1003654926847922176</a></p><p>… and instead of something like that …</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qf44YGx3mIAzelerDE9e6g.jpeg" /></figure><blockquote>from <a href="https://twitter.com/brandonkelly/status/1003394649141252096">https://twitter.com/brandonkelly/status/1003394649141252096</a></blockquote><p>… here some interesting thoughts from @<a href="https://twitter.com/rodneyrehm/status/1003784566467891200">rodneyreh</a>m</p><p>“… maybe MS will take on helping developers monetize their OSS work by providing the necessary infrastructure right in github projects — e.g payment, help w/ licensing, etc? I never understood why github didn’t offer that for a small cut. Make it easy to buy/sell and people will… Which doesn’t mean I want to financially profit from my OSS. I’m more interested in the other side of the transaction. If (micro) payments became as easy as buying shit on amazon, I know I’d have hit that button a bunch of times… And if you’re not into licensing your work that’s fine. it might still be an improvement over gittip, patreon and what not for non-US people/entities (regarding taxes), considering MS has local offices world wide…” — <a href="https://twitter.com/rodneyrehm/status/1003784566467891200">https://twitter.com/rodneyrehm/status/1003784566467891200</a></p><h3>— — — — — — — — — <br>Why was github sold?<br> — — — — — — — — —</h3><h4>1. Because of the money</h4><p>…nobody ( maybe Richard Stallman is a exception here ;-) ) would say “no” for such much money.</p><h4>2. Because of us….</h4><p>I didn’t pay for the github service and I think there are many developers who never payed for it. So if you didn’t pay for whatever, you will pay in a different way or maybe in a different time.</p><p>Examples:</p><p>- “free” ad-blocker (maybe one day they sell your browser history) — <a href="https://www.ghacks.net/2016/11/01/browsing-history-sold/">https://www.ghacks.net/2016/11/01/browsing-history-sold/</a></p><p>- “free” comment system (maybe add ads in your site) — <a href="https://wptavern.com/disqus-hits-sites-with-unwanted-advertising-plans-to-charge-large-publishers-a-monthly-fee-to-remove-ads">https://wptavern.com/disqus-hits-sites-with-unwanted-advertising-plans-to-charge-large-publishers-a-monthly-fee-to-remove-ads</a></p><p>- “free” rss reader (maybe closed some day) — <a href="https://www.google.com/reader/about/">https://www.google.com/reader/about/</a></p><p>… so there’s no such thing as a free lunch! Sorry for that! Also OSS (Open Source Software) by itself isn’t &gt;&gt;free&lt;&lt;. “To understand the concept, you should think of “free” as in “free speech,” not as in “free beer”. — <a href="http://www.gnu.org/philosophy/free-sw.en.html">http://www.gnu.org/philosophy/free-sw.en.html</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*wZbSOWvO-p5hAaSC2WTuAQ.jpeg" /></figure><p>“$7 a month? That’s crazy. I could build something like 1/32nd as good in just like 2 or 3 hundred hours.” — <a href="https://twitter.com/hanspagel/status/1003635675734921216">https://twitter.com/hanspagel/status/1003635675734921216</a></p><p>Maybe we should all take a few seconds and think about who we can support, so that our product is keep running and can be improved in the future. Do you use any vendor libraries / frameworks (maybe you can thanks the maintainer). Do you use a good IDE with cool plugins, maybe you should also send some money to this developers.</p><h3>— — — — — — — — — — <br>Why we use(d) github?<br> — — — — — — — — — —</h3><h4>1. Because it’s easy to use</h4><p>… and there is good documentation for beginners. But you don’t need any GUI if you will use git. For example at work we only have a git repository, without any web interface. If you decide that the “master”-repository is at server “x” and every developer in the team push there changes there, than “x” is your git server. But you can easily push to other git servers or (if ssh is open and you know the password) you can directly push into local coworker git repository (please do not do that and do not — force).</p><p>… but this is also true: “Github (or any centralized service) just makes it easy and convenient to follow some best practices like peer review, publishing releases, managing user feedback, CI integration… know of any tools that can help make these easier to do with a DCVS philosophy?” — <a href="https://twitter.com/TwuniMusic/status/1003664551265484801">https://twitter.com/TwuniMusic/status/1003664551265484801</a></p><h4>2. Because of “social”-coding.</h4><p>- May 4, 2010 registered my github account (one year after I registered my twitter account). So ~8 years ago I started to improve my smartphone by compiling / patching the android kernel and android itself.</p><p>- Begin of <strong>2011</strong> I released Mod+X (<a href="https://suckup.de/2011/02/android-2-2-3-froyo-modx-spicagenmod-rc1-samsung-galaxy-spica-i5700/">https://suckup.de/2011/02/android-2-2-3-froyo-modx-spicagenmod-rc1-samsung-galaxy-spica-i5700/</a>) it was a Android 2.2.2 Froyo | SpicagenMod with customization exactly for that phone. (e.g. long press the volume-up key and the next song will be played). And I updated the kernel from 2.6.29 to 2.6.x (1467 commits ahead) <a href="https://github.com/voku/i5700-sm-kernel">https://github.com/voku/i5700-sm-kernel</a> — At that time I got really cool feedback and that motivated me. e.g.: “Awesome! Hope you are working for Samsung and get a shitload of money for it :) “ And no, I never got even one $ for it.</p><p>- In <strong>2012</strong> I had a new phone “Samsung Galaxy S2” and I started to build also a kernel for that device. From my code e.g. 680 commits was merged into “Dorimanx-SG2-I9100-Kernel” (<a href="https://github.com/dorimanx/Dorimanx-SG2-I9100-Kernel/commits?author=voku">https://github.com/dorimanx/Dorimanx-SG2-I9100-Kernel/commits?author=voku</a>) I really enjoyed that time, because I learned so much new things. Thanks to Yuri.Sh from Israel and many other developers I had a really good social coding experience. And no, I never got even one $ for it.</p><p>- In <strong>2013</strong> I created (and forked) my first public php libraries. And the cool think was you can see how many developers are downloading your code via composer. <a href="https://packagist.org/packages/voku/">https://packagist.org/packages/voku/</a> And I could use my own libraries on work, so this was a win-win situation for me. And no, I never got even one $ for it.</p><p>- In <strong>2014</strong> I was still at the same employer @ Düsseldorf and still publishing new php libraries. e.g.: the “Portable UTF-8” <a href="https://github.com/voku/portable-utf8">https://github.com/voku/portable-utf8</a> … strlen(‘fòôbàř’); // 10 … and I realized how much is broken in the web and I was still keep learning many things, especially by reading other peoples code on github. And I also created a “.dotfile”-repository, like everyone else (<a href="https://github.com/voku/dotfiles">https://github.com/voku/dotfiles</a> — 685 commits since then) and I learned to love my toolchain.</p><p>- In <strong>2015</strong> I was still here and try to improve my coding-skills and I realized “ninety percent of everything is crap”. So I released e.g. there ＠ + @ are allowed in a e-mail address (<a href="https://github.com/voku/email-check">https://github.com/voku/email-check</a>) and that encoding is a hard thing “&lt;IMG SRC=&amp;#x6A&amp;#x61&amp;#x76&amp;#x61&amp;#x73&amp;#x63&amp;#x72&amp;#x69&amp;#x70&amp;#x74&amp;#x3A&amp;#x61&amp;#x6C&amp;#x65&amp;#x72&amp;#x74&amp;#x28&amp;#x27&amp;#x58&amp;#x53&amp;#x53&amp;#x27&amp;#x29&gt;” (<a href="https://github.com/voku/anti-xss">https://github.com/voku/anti-xss</a>)</p><p>- In <strong>2016</strong> I was still learning e.g. how to compress HTML5 code (<a href="https://github.com/voku/HtmlMin/blob/master/src/voku/helper/HtmlMin.php#L525">https://github.com/voku/HtmlMin/blob/master/src/voku/helper/HtmlMin.php#L525</a>) or how to use “dot-notation” for accessing arrays (<a href="https://github.com/voku/Arrayy#set-values-via-arrayy-syntax-dot-notation">https://github.com/voku/Arrayy#set-values-via-arrayy-syntax-dot-notation</a>) … and exploring pitfalls from DOMDocument (<a href="https://github.com/voku/simple_html_dom">https://github.com/voku/simple_html_dom</a>).</p><p>- In <strong>2017</strong> I was still learning new things switched all my php projects to php7, started created my own content-management-system (on gitlab — private repository, not released yet … and I can’t recommend to build one more, because there are so many… so maybe it will be a framework with some cms features, we will see), released e.g. <a href="https://github.com/voku/HtmlFormValidator">https://github.com/voku/HtmlFormValidator</a> … switched to a e-commerce company and married my girlfriend. ❤</p><p>- In <strong>2018</strong> I learned by my own that open source software is mostly better tested then closed source software, because when it’s public you want to publish something helpful (e.g. with docs + tests) and beautiful (e.g. code-style). And for example the “Portable UTF-8” php library is installed 360.000 times and so it’s also tested in 360.000 times in different environments and with different php versions etc. so it’s also good tested by users. I discovered how I can use php OpCache by simply using file_put_contents +include. (<a href="https://github.com/voku/simple-cache/commit/15446f048ce7b8b84114ed947a59a03998dbcb93#diff-0e0e8cc4c23ee52b9c4dd9e0289f3571">https://github.com/voku/simple-cache/commit/15446f048ce7b8b84114ed947a59a03998dbcb93#diff-0e0e8cc4c23ee52b9c4dd9e0289f3571</a>) And since this year we have a little daughter. ❤ ❤ ❤</p><p>I am still here: while(learning &amp;&amp; coding) … but without much money and no, I never got even one € for my open source stuff. So if you have (paid) webdev /work, please contact me.</p><p>… and maybe you will support me?</p><ul><li><a href="https://liberapay.com/voku/donate">https://liberapay.com/voku/donate</a></li><li><a href="https://www.patreon.com/voku">https://www.patreon.com/voku</a></li></ul><p>… or star one of my projects?</p><ul><li><a href="https://github.com/voku">https://github.com/voku</a></li></ul><p>… follow me on social media?</p><ul><li><a href="https://moelleken.org/Kontakt/Social-Links/">https://moelleken.org/Kontakt/Social-Links/</a></li></ul><p>… read my blog?</p><ul><li><a href="https://suckup.de/">https://suckup.de/</a></li></ul><p>… write me a e-mail?</p><ul><li>lars@moelleken.org</li></ul><h3>— — — — — — — — — — — — —<br>Why we shouldn’t use github?<br> — — — — — — — — — — — — —</h3><p>“The main problem with Github being bought by Microsoft isn’t MS. It’s that in 2018 we still haven’t learned that critical infrastructure shouldn’t belong to one company … and that we should avoid building single points of failure.” — <a href="https://twitter.com/tante/status/1003615096910893056">https://twitter.com/tante/status/1003615096910893056</a></p><p>“Git (distributed) turned to Github (centralized). Just as email turned to Gmail. Just as ‘storing files on web servers’ became ‘Dropbox’.” — <a href="https://twitter.com/tante/status/1003615387559358464">https://twitter.com/tante/status/1003615387559358464</a></p><p>“You know if peopled used git like it’s supposed to be used, this whole github thing wouldn’t even be a concern. I’ve been concerned about github being a centralized place for a DVCS for a while now.” — <a href="https://twitter.com/BeardOps/status/1003663508137918469">https://twitter.com/BeardOps/status/1003663508137918469</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e3b3f0da65c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ubuntu Windows .dotfiles]]></title>
            <link>https://medium.com/@voku/ubuntu-windows-dotfiles-c8dc2f50117?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8dc2f50117</guid>
            <category><![CDATA[how-to]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Wed, 05 Apr 2017 22:45:30 GMT</pubDate>
            <atom:updated>2017-04-05T22:45:30.518Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*OgDSr8mmW-vtoT_J." /></figure><p><a href="http://ift.tt/2nLzZFf">Ubuntu + Windows + .dotfiles</a></p><p><strong>1.</strong> Open „Update &amp; Security“ &gt; „For Developers“ &gt; activate the „Developer Mode“.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*M2194YmOrzRoUi-i." /></figure><p><strong>2.</strong> Under „Programs“ &gt; „Programs and Features“, click “Turn Windows Features On or Off”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*HSF3afc15056uXN2." /></figure><p><strong>3.</strong> Enable the “Windows Subsystem for Linux (Beta)” option in the list and click “OK”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*t-Op8Z31fCzgXbcJ." /></figure><p><strong>4.</strong> After an „reboot“, you open the command line (cmd) and type „bash“ and press [Enter].</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*SarHI7PJlZwpmv-m." /></figure><p><strong>5.</strong> Enter a „Username“ and a „Password. (you need the password for „sudo“-commands)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*Bovoy2wtxjFdqotn." /></figure><p><strong>6.</strong> Now you can open „Bash on Ubuntu on Windows“.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/190/0*xpxlEDFW68uVSuvq." /></figure><p><strong>7.</strong> Install „git“ via:</p><pre>sudo apt-get install git</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*56bVN_smofMJ8qH_." /></figure><p><strong>8.</strong> Get the „.dotfiles“ via:</p><pre>cd ~ ; git clone http://ift.tt/2oEzi4q; cd dotfiles</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*X8oSOOqyqgXkmvxo." /></figure><p><strong>9.</strong> Install dependencies via:</p><pre>./firstInstallDebianBased.sh</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*RJjDOJNyUQmn34Gw." /></figure><p><strong>10.</strong> Install zsh (optional, but recommended).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*4_xQwyDccCcf6Ski." /></figure><p><strong>11.</strong> Use the „.dotfiles“ via:</p><pre>./bootstrap.sh</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*siHUBU_OSwoMYJhU." /></figure><p><strong>12.</strong> Download, install and configure „ConEmu“ …</p><p>… a alternative „Command Line GUI“ for Windows. Instead of the default GUI we have UTF-8 support with „ConEmu“, so we can use chars like „✓“.</p><p>-&gt; <a href="http://conemu.github.io/">http://conemu.github.io</a></p><p>After you have installed it, we can force „bash“ to „zsh“ in the configuration.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*awgmz1gNgBJDQysb." /></figure><p><strong>13.</strong> Add some „symlinks“ for easier navigation via: e.g.</p><pre>sudo ln -s /mnt/c /c</pre><pre>sudo ln -s /mnt/d /d</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/0*qIrXVhk1dM_fYOLf." /></figure><p><strong>14.</strong> Add the current „hostname“ into the „/etc/hosts“-file.</p><pre>sudo su</pre><pre>echo &quot;127.0.0.1 $(hostname)&quot; &gt;&gt; /etc/hosts</pre><pre>exit</pre><p>Now you can edit your „.dotfiles“-settings and tweak your configuration.</p><p>Here is my example-config: <a href="http://ift.tt/2p2EKL0">http://ift.tt/2p2EKL0</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1/0*tAUSNVmUJHLVhphJ." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/77/0*3A6e3RxPdB2FmG_4." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/62/0*YR_2s05_8IBG1BBZ." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/113/0*1RFvPuPcyBnV4ynC." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/64/0*rEvFnZYJ6C70D3hM." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1/0*NVWZ4TZQZ3a_88Ak." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1/0*3eUR2XoRgYQuRcmn." /></figure><p><em>Originally published on </em><a href="http://ift.tt/2nLzZFf"><em>Wordpress</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8dc2f50117" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I /]]></title>
            <link>https://medium.com/@voku/i-e25db9b6a126?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/e25db9b6a126</guid>
            <category><![CDATA[bash]]></category>
            <category><![CDATA[bashrc]]></category>
            <category><![CDATA[linux]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Wed, 17 Feb 2016 21:52:58 GMT</pubDate>
            <atom:updated>2016-02-17T21:52:58.857Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*0p2hDntZbmIEs6Kf." /></figure><p><a href="http://ift.tt/1ROolVD">I ❤ ~/</a></p><p>Vor einiger Zeit habe ich bereits einen Blog-Post über meine „<a href="http://ift.tt/1lZSYDa"><strong>.dotfiles</strong></a>“ geschrieben, jedoch habe ich mich damals mehr auf die Installation beschränkt und habe keine Beispiele gezeigt, so dass man den Vorteil der .dotfiles nur schwer erfassen konnte.</p><p>Außerdem benötigt man ggf. nur einige Funktionen oder Dateien um sein System an an seine Bedürfnisse anzupassen. Auf der Webseite <a href="http://ift.tt/Wfl1t0">GitHub ❤ ~/</a> gibt es sehr gute .dotfiles Sammlungen, welche man einfach „<a href="http://ift.tt/1znkBTb">forken</a>“ und anpassen und nutzen kann.</p><p>PS: wer sich bisher nicht mit „<a href="http://ift.tt/1BrtcTG">git</a>“ auskennt, sollte sich einmal eines der folgenden interaktiven git-Tutorials anschauen:<br> — <a href="https://try.github.io/">try.github.io/</a><br> — <a href="http://ift.tt/1861dZD">http://ift.tt/1861dZD</a><br> — <a href="http://ift.tt/1IfUIWn">gitreal.codeschool.com/</a></p><h4>Beispiele zu meinen .dotfiles:</h4><p><strong>Navigation:</strong><br><a href="http://ift.tt/1IfUKO0">http://ift.tt/1IfUKO0</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*5sllcoCfnE0ECpYH." /></figure><p><strong>Farben für die Kommandozeile:</strong><br><a href="http://ift.tt/1IfUL4y">http://ift.tt/1IfUL4y</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*ggWnCdrd5f3jL1h3." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*MB3fB-8qyjC73aNh." /></figure><p><strong>Nützliche Shortcuts: z.B. date_*</strong><br><a href="http://ift.tt/1Dz1jve">http://ift.tt/1Dz1jve</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*ZHuQ4_0_RrSB2DWK." /></figure><p><strong>Copy&amp;Past via „getclip“ und „putclip“:</strong> <a href="http://ift.tt/1Dz1jLy">http://ift.tt/1Dz1jLy</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*zfAlBzdtkO5tpM4G." /></figure><p><strong>kill &amp; pkill via z-shell:</strong> <a href="http://ift.tt/1Dz1hmZ">http://ift.tt/1Dz1hmZ</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*IOFf98QkXTK_Ha6b." /></figure><p><strong>grep &amp; ack:</strong> <a href="http://ift.tt/1IfULBA">http://ift.tt/1IfULBA</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*3Mc2n0_Sx2HPFl_z." /></figure><p><strong>git via z-shell:</strong> <a href="http://ift.tt/1Dz1hDn">http://ift.tt/1Dz1hDn</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*fUbXk1QVABHsAMUI." /></figure><p><strong>„ls“-aliases:</strong> <a href="http://ift.tt/1Dz1jLI">http://ift.tt/1Dz1jLI</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*CJijPnAvZcAdqXCx." /></figure><p><strong>vim » Automatisch zur letzten Position springen </strong><br><a href="http://ift.tt/1Dz1k20">http://ift.tt/1Dz1k20</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*DEhnlRZtq0wwB-3q." /></figure><p><strong>vim » Schell-Suche via „#“</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*eTdpjqBjxBoB3SiZ." /></figure><p><strong>vim » markieren Leerzeichen am Ende der Zeile</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*dDm-nuia5vPKwUdu." /></figure><p><strong>vim » Tabs nutzen</strong><br><a href="http://ift.tt/1Dz1k26">http://ift.tt/1Dz1k26</a><br><strong>vim » NERD Tree via „,“ + „-„</strong><br><a href="http://ift.tt/1Dz1k28">http://ift.tt/1Dz1k28</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*mG9dDkln6hlK6fMo." /></figure><p><strong>vim » Autovervollständigung via Tabulatur-Taste</strong><br><a href="http://ift.tt/1Dz1hTW">http://ift.tt/1Dz1hTW</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*msgGJSFxbVr9IPaQ." /></figure><p>Erklärungen zu meinen .dotfiles:</p><h4><a href="http://ift.tt/1IfUMW9"><strong>.bash_profile</strong></a><strong> &amp; </strong><a href="http://ift.tt/1IfUMoW"><strong>.bashrc</strong></a><strong> // </strong><a href="http://ift.tt/1Dz1hU0"><strong>.zprofile</strong></a><strong> &amp; </strong><a href="http://ift.tt/1Dz1hU4"><strong>.zshrc</strong></a></h4><p>Wenn man sich direkt über die Konsole einloggt (ohne GUI) oder remote über ssh, dann wird die Datei: „~/.bash_profile“ (bash) bzw. „~/.zprofile“ (zsh) ausgeführt, um Ihre Shell vor der ersten Eingabeaufforderung zu konfigurieren.<br> Wenn man jedoch bereits angemeldet ist und ein neues Terminalfenster z.B. in Gnome oder KDE öffnet, dann wird die „~/.bashrc“ bzw. „~/.zshrc“-Datei ausgeführt. Außerdem wird die „.[bash|z]rc“ auch ausgeführt, wenn man eine neue Bash-Instanze via „/bin/bash“ in einem Terminal ausführt. Um zu verhindern, dass unsere Einstellungen automatisierte Skripte negativ beeinflussen, führen wir die folgenden Anpassungen nur bei interaktiven Konsolen aus.</p><p>In meiner „.bashrc“-Datei ist nicht viel zu sehen, da diese Datei auf die „.bash_profile“-Datei zugreift, so dass man bei Änderungen nur eine Datei bearbeiten muss. In „<a href="http://ift.tt/1Dz1hU6">.bash_profile#L19</a>“ werden wiederum andere Dateien geladen, so dass wir die selben „Funktionen“ und „Aliases“ sowohl in der „<a href="http://ift.tt/1Dz1kiE">Bash</a>“ als auch in der „<a href="http://ift.tt/1oGccBg">Z-Shell</a>“ verwenden können.</p><h4><a href="http://ift.tt/1Dz1kiF"><strong>.config_dotfiles</strong></a></h4><p>In dieser Datei kann man Einstellungen für meine .dotfiles vornehmen. Wenn du z.B. deinen Standard-User-Namen in der Variable „CONFIG_DEFAULT_USER“ hinterlegst, wird diese nicht mehr in der Shell-Prompt angezeigt.</p><h4><strong>.path</strong></h4><p>In dieser Datei kannst du Pfade zu deinen Ausführbaren Dateien einfügen, so dass du diese ohne Angabe des Pfades ausführen kannst.</p><p>Diese Datei ist nicht im öffentlichen Repository und ist via „<a href="http://ift.tt/1Dz1kiH">.gitignore</a>“ aus git verbannt, da es vertrauliche Informationen enthalten kann. z.B.: export PATH=“$HOME/utils:$PATH“</p><h4><a href="http://ift.tt/1Dz1hU8"><strong>.load</strong></a></h4><p>In dieser Datei laden wir weitere optionale Einstellungen, welche ggf. gar nicht installiert und somit auch nicht geladen werden müssen.</p><h4><a href="http://ift.tt/1Dz1kiJ"><strong>.colors</strong></a></h4><p>Hier definieren wir einige Farben, welche dann in den folgenden Dateien verwendet werden können. z.B.: COLOR_BLACK=‚\e[0;30m‚</p><h4><a href="http://ift.tt/1Dz1hUa">.exports</a></h4><p>Variablen in Shell-Scriptes verhalten sich so ähnlich wie in JavaScript. Variablen sind Standardmäßig „global“, dass heißt selbst Variable innerhalb einer Funktion sind anschließend auch außerhalb der Funktion verfügbar. Daher verwenden wir „<a href="http://ift.tt/1Dz1kiL">local</a>“ innerhalb von Funktionen, für Variablen welche nur innerhalb der Funktion zur Verfügung steht sollen. Außerdem können wir Variablen via „<a href="http://ift.tt/1Dz1iaq">export</a>“ exportieren, so dass diese nicht nur zur Ausführungszeit, sondern anschließend ebenfalls allen Kindprozessen als Umgebungsvariable zur Verfügung stehen.</p><h4><a href="http://ift.tt/1Dz1kiP">.icons</a></h4><p>Wie die Farben werden auch die entsprechenden Icons exportiert, so dass wir diese anschließend in Funktionen etc. nutzen können.</p><h4><a href="http://ift.tt/1Dz1iau">.aliases</a></h4><p>In dieser Datei werden Abkürzungen für komplexe Befehle oder / und Fallbacks definiert, welche anschließend als Befehl ausgeführt werden.</p><p>z.B.:<br> alias starwars=’telnet towel.blinkenlights.nl‘<br> alias sgrep=’grep -R -n -H -C 5 –exclude-dir={.git,.svn,CVS}‘</p><h4><a href="http://ift.tt/1Dz1iaw">.bash_complete</a> // <a href="http://ift.tt/1IfUNta">completion.zsh</a></h4><p>Auch wenn die „Z-Shell“ (zsh) bessere <a href="http://ift.tt/1IfUPBa"><em>Tab</em>completion</a> bietet, kann man auch in der „Bash“ einige Tabcompletion-Features nutzen.</p><p>z.B.:</p><pre>apt- [Tabulator]<br>apt-g [Tabulator]<br>apt-get [Tabulator]<br>apt-get i [Tabulator]<br>apt-get install [Tabulator] [Tabulator]<br>apt-get install apa [Tabulator]</pre><h4><a href="http://ift.tt/1iYXH7C">.functions</a></h4><p>Hier habe ich einige praktische Funktionen gesammelt, welche ich öfters nutze und nicht als Plugin auslagern möchte.</p><p>z.B.:<br><a href="http://ift.tt/1IfUNJq">lman()</a> -&gt; „Open the manual page for the last command you executed“<br><a href="http://ift.tt/1IfUNJs">netstat_free_local_port()</a> -&gt; „get one free tcp-port“<br><a href="http://ift.tt/1IfUPBi">iptablesBlockIP()</a> -&gt; „block a IP via ‚iptables’“</p><p><em>Überlegungen:</em> In der „normalen“ Programmierung nutzt man lowerCamelCase für normale Variablen / Funktionen und Methoden, jedoch sind eigentlich alle Funktionen in der Shell lowercase z.B.: whoami, updatedb, killall. Ich mich dafür entschieden mich bei neuen Funktionen und Aliases an einem Original-Befehl zu orientieren, so dass man diese nicht neu lernen muss, sondern diese via [Tabulator] nutzen kann.</p><h4>.extra</h4><p>Auch diese Datei ist nicht im öffentlichen Repository und ist via „<a href="http://ift.tt/1Dz1kiH">.gitignore</a>“ aus git verbannt, da es vertrauliche Informationen enthalten kann. Hier kann man alle zuvor gesetzten Variablen, Einstellungen, Funktionen überschreiben oder ergänzen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*uCcA4162zGi8Zejw." /></figure><h4>Ähnliche Beiträge:</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*lw9bjscifXoFkdy7.png" /></figure><p><a href="http://ift.tt/1ROokB0">my dotfiles</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*IytPhOuFOzYmIBb9.png" /></figure><p><a href="http://ift.tt/1ROomc8">.zshrc</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*TY7_S3yKkZiqcOoQ.png" /></figure><p><a href="http://ift.tt/219feRT">Kurztipp: lokalen offenen Port finden (bash)</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*vXVy3axIbKU-N1b6." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*UnudPuxmuRjY1OYP." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*GIj9SdqOyYZtTPMt." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*GdkLYDHBuMffKWhO." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*UyRohVIUgPN_HHFq." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*peuoebXJU4Cse5vr." /></figure><p><em>Originally published on </em><a href="http://ift.tt/1ROolVD"><em>Wordpress</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e25db9b6a126" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[gzip für Webseiten]]></title>
            <link>https://medium.com/@voku/gzip-f%C3%BCr-webseiten-5be257f2a458?source=rss-5d6a4e584b43------2</link>
            <guid isPermaLink="false">https://medium.com/p/5be257f2a458</guid>
            <category><![CDATA[linux]]></category>
            <category><![CDATA[apache]]></category>
            <category><![CDATA[html]]></category>
            <dc:creator><![CDATA[Lars Moelleken]]></dc:creator>
            <pubDate>Wed, 17 Feb 2016 21:52:49 GMT</pubDate>
            <atom:updated>2016-02-17T21:52:49.535Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*Z8GpToPRc91yXJdU.gif" /></figure><p><a href="http://ift.tt/1ROolot">„gzip“ für Webseiten</a></p><p>Um das Verhalten von „<a href="http://ift.tt/Wvsw8F">gzip / deflate</a>“ besser zu verstehen, möchte ich als erstes ganz kurz auf den Algorithmus hinter der Komprimierung eingehen. Dafür muss man zumindest zwei Grundlegende Theorien verstehen. Zum einen die sogenannte <a href="http://ift.tt/P5Id9g">Huffman-Kodierung</a>, dabei geht darum Text mit möglichst wenig Bits (0 || 1) zu übersetzen.</p><p><a href="http://ift.tt/1vQY5ka">Es folgt ein einfaches Beispiel</a>:</p><p><strong>String:</strong> „im westen nichts neues“</p><p>Die Länge des Strings beträgt 22 Zeichen, was bei einem 4-Bit-Code (0000, 0001 …) eine Anzahl von 22 * 4 Bit = 88 Bit ergeben würde.</p><p>Berücksichtigt man jedoch die Häufigkeit der wiederkehrenden Zeichen, kann man für bestimmte Zeichen, welche oft im String vorkommen, kürzere Bit-Codes nutzen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*eJfibPSvF2RberQx.gif" /></figure><p>Als erstes Zählen wir die Anzahl der vorkommenden Zeichen. Die Zeichen welche die geringste Häufigkeit aufweisen werden anschließend als erstes miteinander Verknüpft und jeweils addiert.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*OwyhIUd5bdufzPfK.gif" /></figure><p>Wie man bereits erkennt bekommen die Zeichen welche nicht so häufig auftreten längere Strecken und somit gleich auch längere Bit-Codes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*rsGjm5RkTatambTR.gif" /></figure><p>Wenn man alle Zweige verknüpft hat, entsteht daraus dann ein Binär-Baum, welcher von oben nach unten gelesen wird.</p><p>Die eindeutigen Bit-Codes sind nun z.B.: 00 für e, 100 für n, 1011 für t und so weiter. Nun benötigen wir nur noch 73 Bit anstatt 88 Bit, um den String binär darzustellen. ;)</p><p>Das zweite Prinzip hat auch mit Wiederholung von Zeichen zu tun. Die sogenannte „<a href="http://ift.tt/1FvvfF8">LZ77″</a> Kompression, welche wiederkehrende Muster in Strings erkennt und durch Referenzen von vorherigen Strings ersetzt.</p><p><a href="http://ift.tt/V6zQxB">Es Folgt ein einfaches Beispiel</a>:</p><p><strong>String:</strong> „Blah blah blah blah blah!“</p><pre>vvvvv     vvvvv     vvv<br>Blah blah blah blah blah!<br>      ^^^^^     ^^^^^</pre><p>Wenn man nun die Wiederholung durch eine Referenz angibt, welche aus der Länge des Wortes (“ blah“ = 5) und Länge der Wiederholungen. („lah blah blah blah“ = 18). Daraus ergibt sich anschließend der folgende komprimierte Text.</p><pre>Blah b(5,18)!</pre><p>… aber nun endlich zum praktischen Teil! Es folgen ein paar Beispiele und Überlegungen im Zusammenhang mit Webseiten / Webservern.</p><h4>1.) „gzip“, ABER weniger ist manchmal mehr</h4><p>Bei kleinen Webseiten und schwachen V-Servern kann es vorkommen, dass die serverseitige Komprimierung im Gesamtkontext keinen Geschwindigkeitsvorteil bietet, da die Komprimierung ggf. mehr Overhead (Payload, CPU-Rechenzeit, Verzögerung der Auslieferung) mit sich bringt als es Vorteile (geringere Dateigröße) bietet. Zumal viele Dateien (Bilder, CSS, JS) nach dem ersten Seitenaufruf im Cache sind und nicht wieder vom Server ausgeliefert werden müssen. Gerade für mobile Geräte macht die Komprimierung jedoch fast immer Sinn, daher würde ich die Komprimierung immer aktivieren!</p><p>PS: hier noch ein Blog-Post zum Thema <a href="http://ift.tt/1FvvfFa">Webseiten Beschleunigen</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*ECI0H4_XBz5TT7Ea.jpg" /><figcaption>mit gzip</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*8pnI6w1Nil2PoHpW.jpg" /><figcaption>ohne gzip</figcaption></figure><p>GRÜN = Download<br>GELB = Wartet<br>BLAU = Verbindung zum / vom Server</p><p>Bei „nginx“ gibt es zudem ein Modul (<a href="http://ift.tt/1vQY3si">HttpGzipStaticModule</a>) welches direkt vorkomprimierte Dateien ausliefert, anstatt diese bei Anfrage zu komprimieren. Jedoch muss man bisher selber dafür sorgen, dass eine entsprechende Datei mit dem selben Timestamp vorhanden ist. (z.B.: main.css &amp; main.css.gz) Falls jemand hier eine Alternative oder ein einfaches Script zum erstellen der zu komprimierenden Dateien hat, meldet euch bitte bei <a href="http://ift.tt/1FvvfVs">mir</a> oder einfach in den Kommentaren.</p><h4>2.) „gzip“, ABER nicht für alles</h4><p>Zunächst sollte man bereits komprimierte Dateien (z.B. JPEG, PNG, zip etc.) nicht noch zusätzlich via „gzip“ ausliefern, da die Dateigröße im schlimmsten Fall noch steigen kann oder zumindest wird für sehr wenig Dateigröße, sehr viel CPU-Zeit auf der Server (Komprimierung) und auf dem Client (Dekomprimierung) verbraucht.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*TJkrk4scB6QjD-AX.png" /></figure><p>Ich empfehle an dieser Stelle mal wieder die „<a href="http://ift.tt/1mqKQNf">.htaccess</a>„-Datei vom HTML5-Boilerplate, welche bereits viele Einstellungen für den Apache-Webserver liefert. Außerdem gibt es dort auch eine optimierte Konfiguration „<a href="http://ift.tt/1vQY5AA">nginx.conf</a>“ für den nginx-Webserver.</p><h4>3.) „gzip“, ABER nicht immer</h4><p>Die „gzip“-Komprimierung kann Ihre Stärken bei größeren Dateien viel besser ausspielen, da wir ja bereits im theoretischen Teil gelernt haben, dass die Komprimierung auf Wiederholungen aufbaut und wenn wenig wiederkehrende Zeichen und Muster vorhanden sind lässt es sich schlecht komprimieren.</p><p>Beim „nginx“-Webserver kann man die minimale zu bearbeitende Dateigröße angeben, sodass eine 6 Byte große Datei mit dem Inhalt „foobar“ bei schwacher „gzip“ nicht auf 40 Byte vergrößert wird.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*CCJYi4SzQmRBbIsv.png" /></figure><h4>4.) „gzip“, ABER nicht so hoch</h4><p>Zudem sollte man nicht mit der höchsten Komprimierungsstufe komprimieren, da man pro Komprimierungsstufe immer weniger Erfolg bei immer mehr CPU-Zeit bekommt. Man sollte weniger stark komprimieren und dadurch Rechenleistung /-zeit einsparen, um die Webseite schneller ausliefern zu können.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*OUWEAcaQ1B9KYmDg.png" /></figure><p>test2_1.js.gz (schwache Komprimierung)<br> test2_2.js.gz (normale Komprimierung)<br> test2_3.js.gz (starke Komprimierung)</p><h4>5.) „gzip“ + Minifizierung</h4><p>Durch Minifizierung von CSS und JS kann man die Dateigröße ebenfalls reduzieren, auch wenn man nicht an die Dateigröße von komprimierten Dateien herankommt, außerdem sinkt der Erfolg der Komprimierung, da wir durch die Minifizierung bereits einige Wiederholungen minimiert haben.</p><p>Es folgt ein Beispiel mit „jquery.js / jquery.min.js“:</p><p><strong><em>Zustand</em></strong></p><p><strong><em>Größe der Datei</em></strong></p><p><strong><em>gewonnene Größe durch die Komprimierung</em></strong></p><p>nicht minifiziert + nicht komprimiert Datei</p><p><strong>250 kB (¼ MB)</strong></p><p>–</p><p>nicht minifiziert + schnelle Komprimierung</p><p><strong>76,3 kB</strong></p><p>173,7 kB</p><p>nicht minifiziert + normale Komprimierung</p><p><strong>68,3 kB</strong></p><p>181,7 kB</p><p>nicht minifiziert + hohe Komprimierung</p><p><strong>68 kB</strong></p><p>182 kB</p><p>minifiziert + nicht komprimiert Datei</p><p><strong>82,3 kB</strong></p><p>–</p><p>minifiziert + schnelle Komprimierung</p><p><strong>30,8 kB</strong></p><p>51,5 kB</p><h4>6.) „gzip“ + Komprimierung verbessern</h4><p>Im Gegensatz zur Programmierung, wo man Wiederholungen vermeidet, sollte man im Frontend (HTML, CSS) gezielt Wiederholungen nutzen, um die Komprimierung zu optimieren. So zeigt das folgende Beispiel wie man die Dateigröße durch die korrekte Positionierung von Attributen der HTML-Tags verbessern kann.</p><p>Zwei Dateien mit dem selben Dateigröße, nur die Reihenfolge der Attribute in der zweiten HTML-Dateien („<em>test1_2.html“</em>) wurden nicht in der selben Reihenfolge angegeben.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*-ne4bLOSFaB7-BPL.png" /><figcaption>test1_1.html</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*IxgDvdU_eJhFxrkr.png" /><figcaption>test1_2.html</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*TBOgSACa11ypoDqw.png" /></figure><p>Wie man in dem Bild erkennen kann, haben wir die Dateigröße der HTML-Datei um zirka 10% reduziert indem wir den Komprimierungsalgorithmus bei der Arbeit unterstützen. ;)</p><h4>Videos:</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*5FLzon2kILXWtS8Q." /></figure><h4>Ähnliche Beiträge:</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*A7dueNCzzZ7-UHhU.gif" /></figure><p><a href="http://ift.tt/219fekR">Webseiten beschleunigen — Übersicht</a><a href="http://ift.tt/219fbWn"></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*utaGotAWQIflkJ-J.png" /></figure><p><a href="http://ift.tt/219fbWn">Webseiten komprimieren</a><a href="http://ift.tt/1ROoloK"></a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*fc3h3WkbyxIBY7rM.png" /></figure><p><a href="http://ift.tt/1ROoloK">Webseiten Analyse</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*tCnQaLd0hXXqAn7G." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*pKnoUeGVfBVXKRnn." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*pCHgEGpzHCEepJYu." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*atjnnnaUNvWIehiE." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*hzhjLl3t8EZQLJzS." /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*T-GZEuaZjMw1UOra." /></figure><p><em>Originally published on </em><a href="http://ift.tt/1ROolot"><em>Wordpress</em></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5be257f2a458" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>