{"id":1160,"date":"2013-11-28T22:12:56","date_gmt":"2013-11-28T21:12:56","guid":{"rendered":"https:\/\/www.entropywins.wtf\/blog\/?p=1160"},"modified":"2014-03-17T23:22:33","modified_gmt":"2014-03-17T22:22:33","slug":"my-adventures-with-autoloading-in-php","status":"publish","type":"post","link":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/","title":{"rendered":"My adventures with autoloading in PHP"},"content":{"rendered":"<p>This post has as audience developers and will provide readers with insights on how to cleanly autoload classes in PHP.<\/p>\n<p>For a long long time, I\u2019ve been one of those <a href=\"https:\/\/www.mediawiki.org\/\" target=\"_blank\">MediaWiki<\/a> developers that just added classes and file names to <a href=\"https:\/\/www.mediawiki.org\/wiki\/Manual:$wgAutoloadClasses\" target=\"_blank\">$wgAutoloadClasses<\/a>, without really knowing how this made class loading work magically. Sure, I was aware there is this autoloder thing somewhere inside of MediaWiki core, that somehow registers this information with a native PHP function. I however had no clue about how this is typically done, what the up and downsides of the MediaWiki approach are, and what alternatives exist.<\/p>\n<p>Luckily for me, my shell of ignorance in this area was cracked a little over a year ago, as I started working on standalone code. Writing such standalone code was a much bigger awakening from ignorance, though that is definitely a topic of its own. By creating code independent of MediaWiki, I was forced to find a replacement for this $wgAutoloadClasses thing I had become so used to. I went through several steps in how to do this before arriving at my current approach. I\u2019ll briefly outline them and explain what made me switch to a better alternative for each.<\/p>\n<p>The first step was having something quite equivalent to $wgAutoloadClasses. An associative array mapping fully qualified class names to file paths. This is quite a maintenance hassle. Every time you add, move or remove a file, the array needs to be updated. As you can imagine, this is merge conflict hell.<\/p>\n<p>The second approach decreased the maintenance hassle by introducing the assumption that the file name could be derived from the class name. The nice thing about this is that it enforces consistency between file and class names, and also forces all classes that are references from other files to reside in their own file. Both these things where in the coding conventions of my projects already, though without these rules actually being used, nothing would scream at you when you violated them, and thus inconsistency gradually grew over time. One still needed to maintain a list though, and merge conflict hell remained.<\/p>\n<p>And then I learned of <a href=\"https:\/\/github.com\/php-fig\/fig-standards\/blob\/master\/accepted\/PSR-0.md\" target=\"_blank\">PSR-0<\/a>. If you are not familiar with this standard yet as a PHP developer, read it, you are not being serious with yourself if you don\u2019t know it by now. It is the now generally accepted standard on how to autoload PHP code. My second step was already much in the direction of PSR-0, and the main difference in the third one was that I replaced the lists of classes by some small boilerplate code that mapped all classes in a namespace to their PSR-0 derived file paths. This is an example of such code from <a href=\"https:\/\/packagist.org\/packages\/diff\/diff\" target=\"_blank\">Diff<\/a> version 0.9:<\/p>\n<pre>spl_autoload_register( function ( $className ) {\r\n\t$className = ltrim( $className, '\\\\' );\r\n\t$fileName = '';\r\n\t$namespace = '';\r\n\r\n\tif ( $lastNsPos = strripos( $className, '\\\\') ) {\r\n\t\t$namespace = substr( $className, 0, $lastNsPos );\r\n\t\t$className = substr( $className, $lastNsPos + 1 );\r\n\t\t$fileName  = str_replace( '\\\\', '\/', $namespace ) . '\/';\r\n\t}\r\n\r\n\t$fileName .= str_replace( '_', '\/', $className ) . '.php';\r\n\r\n\t$namespaceSegments = explode( '\\\\', $namespace );\r\n\r\n\t$inQueryEngineNamespace = count( $namespaceSegments ) &amp;gt; 1\r\n\t\t&amp;amp;&amp;amp; $namespaceSegments[0] === 'Wikibase'\r\n\t\t&amp;amp;&amp;amp; $namespaceSegments[1] === 'Database';\r\n\r\n\tif ( $inQueryEngineNamespace ) {\r\n\t\t$inTestNamespace = count( $namespaceSegments ) &amp;gt; 2 &amp;amp;&amp;amp; $namespaceSegments[2] === 'Tests';\r\n\r\n\t\tif ( !$inTestNamespace ) {\r\n\t\t\t$pathParts = explode( '\/', $fileName );\r\n\t\t\tarray_shift( $pathParts );\r\n\t\t\tarray_shift( $pathParts );\r\n\t\t\t$fileName = implode( '\/', $pathParts );\r\n\r\n\t\t\trequire_once __DIR__ . '\/src\/' . $fileName;\r\n\t\t}\r\n\t}\r\n} );<\/pre>\n<p>There are some things to note about this particular example.<\/p>\n<p>First of all, I had something like this in all libraries I maintained. If you look closely here, you will see that I forgot to update the name of a variable, which still mentions \u201cQueryEngine\u201d, which I\u2019m now assuming is the component where I copied this code from when I started <a href=\"https:\/\/github.com\/wmde\/WikibaseDatabase\" target=\"_blank\">WikibaseDatabase<\/a>.<\/p>\n<p>Secondly, it has a bunch of code specific to the namespace used. If there where multiple top level namespaces used, this code would become quite a bit more verbose. Another factor adding to the verbosity is the exclusion of the test namespace.<\/p>\n<p>A final issue with this code is that it will load any class in the Wikibase\\Database namespace from the location this code computes. If it is not there, you get an error. This happened quite a few times by misspelling class names in test case, which then causes a \u201cfile not found\u201d error, coming from this autoloding code, leaving it up to you to find where it is actually wrong. On top of that annoyance, the code as is, essentially prevents any extension to this component from defining classes in Wikibase\\Database, as WikibaseDatabase would try to load them from its own src\/ folder.<\/p>\n<p>For some time I\u2019ve been thinking of just ditching these piles of ugly boilerplate autoloading code, and just requiring the consumer of my packages to autoload things as specified in the package definition (ie composer.json). I got this idea after seeing a lot of different libraries do this exact same thing.<\/p>\n<p>What made me hesitant to do so is that binding to any tool, like binding to any framework, is not particularly nice. I want my libraries to be usable without forcing people to also use some particular tool, in this case Composer. Though of course they are not really forced to use Composer. They are only forced to make use of the information in the package definition, which happens to be in Composer format. This can be easily interpreted without using Composer though, as it is a simple json format. Or alternatively the consumer can simply copy the information in whatever autoloading system they use. This also does mean the autoloading system used by the consumer needs to support the kind of loading the library uses. For instance, MediaWiki would be in trouble for most libraries, as it only has classmap autoloading.<\/p>\n<p>A second concern that kept me from doing this is that some kind of action further then including the library entry point is needed to have its autoloading work. Since I mostly work with MediaWiki extensions, and have several libraries that pretend to be MediaWiki extensions, this is problematic, so to the lack of modern autoloading facilities in MediaWiki. Recently I added support for installing extensions via Composer into MediaWiki, though this does require a change in workflow for users. So most people are still using the old workflow, in which the Composer support is not of help to them. I got over this concern by creating some libraries where I simply decided to not care about MediaWiki, and decided that if MediaWiki extensions wanted to use them, then they\u2019d have to deal with any restrictions forced upon them by MediaWiki, rather then my libraries themselves.<\/p>\n<p>Here you have an example of the autoload section of the first release of <a href=\"https:\/\/github.com\/DataValues\/Geo\" target=\"_blank\">DataValues Geo<\/a>, which is using my now preferred autoloading approach:<\/p>\n<pre>{\r\n\t\/\/ ...\r\n\t\"autoload\": {\r\n\t\t\"files\" : [\r\n\t\t\t\"Geo.php\"\r\n\t\t],\r\n\t\t\"psr-0\": {\r\n\t\t\t\"DataValues\\\\\": \"src\",\r\n\t\t\t\"ValueFormatters\\\\\": \"src\",\r\n\t\t\t\"ValueParsers\\\\\": \"src\"\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>As you can see, there still is an entry point file, which will always be included. It however no longer contains an autoloader, it just defines a version constant.<\/p>\n<p>The psr-0 mapping causes the autoloader to look for classes in the DataValues namespace in the src\/DataValues folder. One great thing also being that it will not cause any issues if a class in this namespace is not in there, as indeed is the case for most such classes. And if you are wondering about performance \u2013 Composer has an optimization feature, and there are quite a few psr-0 loaders out there that do as well.<\/p>\n<p>And that is really all you need. No need for lists that are maintenance nightmares and cause pointless merge conflicts. No need for inventing your own boilerplate code that you copy and paste between projects. All the work can be handled by great tools provided by the PHP community, and that you can use in your project today, if you do the little effort to specify where your classes can be found in your package definition.<\/p>\n<p>I\u2019m looking forward to Composer supporting a \u201cnamespace prefix\u201d option as defined by the proposed PSR-4 standard. When using the PSR-0 loading support of Composer and you have all classes of your library in Foo\\Bar\\Baz\\Bah, then you have to put ClassName into src\/Foo\/Bar\/Baz\/Bah\/ClassName.php, while you likely want this to be mapped onto src\/ directly. In such cases I\u2019m currently still using a custom autoloader in the entry point file, or using the automatic classmap support Composer provides.<\/p>\n<p>To end this blog post, a tip to <a href=\"https:\/\/github.com\/sebastianbergmann\/phpunit\/blob\/3.7\/README.md\" target=\"_blank\">PHPUnit<\/a> users: create a <a href=\"http:\/\/phpunit.de\/manual\/3.7\/en\/textui.html\" target=\"_blank\">bootsrapping file<\/a> with something as follows:<\/p>\n<pre>&amp;lt;?php\r\n\r\necho exec( 'composer update' ) . \"\\n\";\r\n\r\nrequire_once( __DIR__ . '\/..\/vendor\/autoload.php' );<\/pre>\n<p>This allows people to download your project, change into its directory, type \u201cphpunit\u201d, and have the tests run. This also makes setting up a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Continuous_integration\" target=\"_blank\">Continuous Integration<\/a> system one step more trivial, and prevents you from having headaches if your code uses a classmap that needs to be regenerated after adding classes.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post has as audience developers and will provide readers with insights on how to cleanly autoload classes in PHP.&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[314,306,315,149,156,195,196],"class_list":["post-1160","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-autoloading","tag-composer","tag-lessons-learned","tag-library","tag-mediawiki","tag-php","tag-phpunit"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>My adventures with autoloading in PHP - Blog of Jeroen De Dauw<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"My adventures with autoloading in PHP - Blog of Jeroen De Dauw\" \/>\n<meta property=\"og:description\" content=\"This post has as audience developers and will provide readers with insights on how to cleanly autoload classes in PHP.&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog of Jeroen De Dauw\" \/>\n<meta property=\"article:published_time\" content=\"2013-11-28T21:12:56+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-03-17T22:22:33+00:00\" \/>\n<meta name=\"author\" content=\"Jeroen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/JeroenDeDauw\" \/>\n<meta name=\"twitter:site\" content=\"@JeroenDeDauw\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeroen\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\"},\"author\":{\"name\":\"Jeroen\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"headline\":\"My adventures with autoloading in PHP\",\"datePublished\":\"2013-11-28T21:12:56+00:00\",\"dateModified\":\"2014-03-17T22:22:33+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\"},\"wordCount\":1379,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"keywords\":[\"Autoloading\",\"composer\",\"Lessons learned\",\"Library\",\"MediaWiki\",\"PHP\",\"PHPUnit\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\",\"name\":\"My adventures with autoloading in PHP - Blog of Jeroen De Dauw\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\"},\"datePublished\":\"2013-11-28T21:12:56+00:00\",\"dateModified\":\"2014-03-17T22:22:33+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.entropywins.wtf\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"My adventures with autoloading in PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/\",\"name\":\"Entropy Wins\",\"description\":\"A blog on Software Architecture, Design and Craftsmanship\",\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\",\"name\":\"Jeroen\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"caption\":\"Jeroen\"},\"logo\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/jeroendedauw\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"My adventures with autoloading in PHP - Blog of Jeroen De Dauw","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/","og_locale":"en_US","og_type":"article","og_title":"My adventures with autoloading in PHP - Blog of Jeroen De Dauw","og_description":"This post has as audience developers and will provide readers with insights on how to cleanly autoload classes in PHP.&hellip;","og_url":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/","og_site_name":"Blog of Jeroen De Dauw","article_published_time":"2013-11-28T21:12:56+00:00","article_modified_time":"2014-03-17T22:22:33+00:00","author":"Jeroen","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/JeroenDeDauw","twitter_site":"@JeroenDeDauw","twitter_misc":{"Written by":"Jeroen","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#article","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/"},"author":{"name":"Jeroen","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"headline":"My adventures with autoloading in PHP","datePublished":"2013-11-28T21:12:56+00:00","dateModified":"2014-03-17T22:22:33+00:00","mainEntityOfPage":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/"},"wordCount":1379,"commentCount":0,"publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"keywords":["Autoloading","composer","Lessons learned","Library","MediaWiki","PHP","PHPUnit"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/","url":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/","name":"My adventures with autoloading in PHP - Blog of Jeroen De Dauw","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#website"},"datePublished":"2013-11-28T21:12:56+00:00","dateModified":"2014-03-17T22:22:33+00:00","breadcrumb":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/28\/my-adventures-with-autoloading-in-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.entropywins.wtf\/blog\/"},{"@type":"ListItem","position":2,"name":"My adventures with autoloading in PHP"}]},{"@type":"WebSite","@id":"https:\/\/www.entropywins.wtf\/blog\/#website","url":"https:\/\/www.entropywins.wtf\/blog\/","name":"Entropy Wins","description":"A blog on Software Architecture, Design and Craftsmanship","publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7","name":"Jeroen","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","caption":"Jeroen"},"logo":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/in\/jeroendedauw\/","https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw"]}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p74TBF-iI","jetpack-related-posts":[{"id":1153,"url":"https:\/\/www.entropywins.wtf\/blog\/2013\/11\/24\/introduction-to-composer-for-mediawiki-developers\/","url_meta":{"origin":1160,"position":0},"title":"Introduction to Composer for MediaWiki developers","author":"Jeroen","date":"2013-11-24","format":false,"excerpt":"This post aims to be a quick start guide for MediaWiki extension developers that want to get their extension to be installable via Composer. If you are not yet familiar with Composer, I recommend you have a look at the Composer page on MediaWiki.org before continuing with this post. Defining\u2026","rel":"","context":"In \"composer\"","block_context":{"text":"composer","link":"https:\/\/www.entropywins.wtf\/blog\/tag\/composer\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2013\/11\/logo-composer-transparent.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2101,"url":"https:\/\/www.entropywins.wtf\/blog\/2018\/01\/20\/php-project-template\/","url_meta":{"origin":1160,"position":1},"title":"PHP project template","author":"Jeroen","date":"2018-01-20","format":false,"excerpt":"Want to start a new PHP project? Perhaps yet another library you are creating? Tired of doing the same lame groundwork for the 5th time this month? Want to start a code kata and not lose time on generic setup work? I got just the project for you! Edit: there\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/01\/php.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/01\/php.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/01\/php.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/01\/php.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/01\/php.png?resize=1050%2C600&ssl=1 3x"},"classes":[]},{"id":1167,"url":"https:\/\/www.entropywins.wtf\/blog\/2013\/12\/25\/funding-for-mediawiki-markdown-extension\/","url_meta":{"origin":1160,"position":2},"title":"Funding for MediaWiki Markdown extension","author":"Jeroen","date":"2013-12-25","format":false,"excerpt":"There currently appears to be no really solid markdown extension for MediaWiki. I have some ideas for one, which are outlined in this blog post. Markdown rendering using a standard PHP markdown rendering library. This means the extension is build on top of solid widely used code that does not\u2026","rel":"","context":"In \"Funding\"","block_context":{"text":"Funding","link":"https:\/\/www.entropywins.wtf\/blog\/tag\/funding\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1782,"url":"https:\/\/www.entropywins.wtf\/blog\/2016\/06\/24\/maps-3-7-for-mediawiki-released\/","url_meta":{"origin":1160,"position":3},"title":"Maps 3.7 for MediaWiki released","author":"Jeroen","date":"2016-06-24","format":false,"excerpt":"I\u2019m happy to announce the immediate availability of Maps 3.7. This feature release brings some minor enhancements. Added rotate control support for Google Maps (by Peter Grassberger) Changed coordinate display on OpenLayers maps from long-lat to lat-long (by Peter Grassberger) Upgraded Google marker cluster library to its latest version (2.1.2)\u2026","rel":"","context":"In &quot;Software&quot;","block_context":{"text":"Software","link":"https:\/\/www.entropywins.wtf\/blog\/category\/software\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/06\/suchrotate.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":921,"url":"https:\/\/www.entropywins.wtf\/blog\/2010\/07\/20\/mediawiki-org-user-page-1-year\/","url_meta":{"origin":1160,"position":4},"title":"MediaWiki.org user page 1 year","author":"Jeroen","date":"2010-07-20","format":false,"excerpt":"Today my MediaWiki.org user page is one year old - I created the first version on July 20, 2009. With my SVN account also approaching it's first birthday, I can now say I'm doing MediaWiki development for a year. A lot has happened in this year. I created the Maps\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1172,"url":"https:\/\/www.entropywins.wtf\/blog\/2014\/01\/02\/mediawiki-extensions-to-define-their-mediawiki-compatibility\/","url_meta":{"origin":1160,"position":5},"title":"MediaWiki extensions to define their MediaWiki compatibility","author":"Jeroen","date":"2014-01-02","format":false,"excerpt":"Over the past year support for real dependency management has been gradually added to MediaWiki and selected extensions. This support being based on the Composer software. While extensions have been able to specify their dependencies for a while, such as PHP libraries and other extensions, they where not able to\u2026","rel":"","context":"In \"composer\"","block_context":{"text":"composer","link":"https:\/\/www.entropywins.wtf\/blog\/tag\/composer\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1160","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/comments?post=1160"}],"version-history":[{"count":3,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1160\/revisions"}],"predecessor-version":[{"id":1259,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/1160\/revisions\/1259"}],"wp:attachment":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/media?parent=1160"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/categories?post=1160"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/tags?post=1160"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}