Virtual Hosts

Instead of using my-project.dev or my-project.local to differentiate domain names, I prefer to use subdomains like local.my-project.com.

This makes more clear what domain I'm developing for and it's easier to develop domain specific features like a redirect for my-project.com or my-project.de domains.

Certificates for localhost with mkcert

create certificates for local development domains with mkcert.exe


Options for DEV Environment

Most modern approach is Docker. Right now I don't have the mental capacity to mess with it, but if, then Devilbox would be the way to go.1

Neard seems to be an all in one portable dev package with Apache, Node.js, versions of php and even browsers to test. I think this could be ok to start with a vanilla webproject, dev machine.

WAMP Server is similar to XAMPP and comes with several PHP versions, but those cannot be run in parallel.

XAMPP is my choice for local development right now, because it runs flawless so far and just needed the option to run PHP 5.6 and any version of PHP 7 in parallel.

This is done by run PHP 5.6 as Apache module as indented and run all other versions in cgi mode.

PHP 5.6 and 7.x parallel on XAMPP

Run PHP 5.6 as Apache Module and PHP 7.4 as CGI

  1. Download PHP 7.x Win32 Thread-Safe https://windows.php.net/download/

  2. Edit httpd-xampp.conf add:

# PHP-CGI setup

# Put Authorization Header back to HTTP Headers
# If you are using Apache with CGI/FastCGI, then you might get an error message about missing authorization headers. This is because Apache does not, by default, pass authorization headers to PHP.

SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0

# PHP Versions

ScriptAlias /php74/ "E:/xampp/php749/"

<IfModule actions_module>
Action application/x-httpd-php74-cgi "/php74/php-cgi.exe"

# Make directory executable
<Directory "E:/xampp/php749/">
    AllowOverride None
    Options None
    Require all denied
    <Files "php-cgi.exe">
          Require all granted
    # Where to find php.ini
    SetEnv PHPRC "E:/xampp/php749"

ScriptAlias /php73/ "E:/xampp/php7321/"

<IfModule actions_module>
Action application/x-httpd-php73-cgi "/php73/php-cgi.exe"

# Make directory executable
<Directory "E:/xampp/php7321/">
    AllowOverride None
    Options None
    Require all denied
    <Files "php-cgi.exe">
          Require all granted
    # Where to find php.ini
    SetEnv PHPRC "E:/xampp/php7321"
  1. Edit httpd-vhosts.conf
<VirtualHost *>
    DocumentRoot E:\WEB\tools.de
    ServerName local.tools.de

    # Run as CGI module
    <FilesMatch "\.php$">
    SetHandler application/x-httpd-php7-cgi
  1. Edit php.ini

    • Enable dynamic extensions (sqlite, curl etc)
    • extension_dir = "ext"
    • Certificate information curl.cainfo="E:\xampp\cacert.pem"
    • Activate Xdebug https://xdebug.org/docs/install
  2. Set PATH to PHP 7.x to use latest PHP version on command line.

  3. Now with PHP 7.x on the cli, set max php version to composer file in project to not accidentally update libraries that need to run on PHP 5, to PHP 7.


    "config": {
        "platform": {
            "php": "5.6.1"
    "require": {
        "symfony/yaml": "^3.2",

Check with composer outdated what needs to be updated.

Path=C:\Program Files (x86)\Common Files\Oracle\Java\javapath;E:\xampp\Git\mingw32\bin;M:\foobar2000\encoders;E:\xampp\FTP;C:\ProgramData\Oracle\Java\javapath;E:\xampp\ImageMagick;E:\xampp\putty;E:\xampp\ghostscript\bin;E:\xampp\FTPSync;E:\xampp\PHPloy;E:\xampp\Git\cmd;E:\xampp\php7321;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;E:\xampp\Console2;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\QuickTime\QTSystem\;C:\ProgramData\ComposerSetup\bin;C:\Users\mo\AppData\Local\Pandoc\;C:\Users\mo\AppData\Roaming\npm;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\;E:\xampp\nodejs\;%SYSTEMROOT%\System32\OpenSSH\;C:\Program Files (x86)\Gource\cmd;C:\ProgramData\chocolatey\bin;C:\Program Files\Microsoft SQL Server\120\Tools\Binn\;C:\Program Files (x86)\PDFtk\bin\;C:\Program Files\dotnet\


Change Composer Home Folder https://w3guy.com/change-composer-global-package-install-folder-windows/

Fake Platform, Ignore Extensions

Fake platform in config.platform in composer.json or use --ignore-platform-reqs when on cli php7.3-cli composer.phar update --ignore-platform-reqs

Example: Missing ext-imagick

ext-imagick is not installed but is a requirement for a package. Install the polyfill calcinai/php-imagick and set the config.platform key to the version. This way ext-imagick is seen as available, even if it's just the polyfill.

repositories key just loads a fork of calcinai/php-imagick that included some additional methods not in the original polyfill. (My changes are merged by now https://github.com/calcinai/php-imagick/pull/7)

    "require": {
        // "dev-feature" is the name of the branch
        "calcinai/php-imagick": "dev-feature"
    "config": {
        "ext-imagick": "3.4.4"
    "repositories": [
            "type": "vcs",
            "url": "https://github.com/marcus-at-localhost/php-imagick"


#php #development




PHP CodeSniffer and PHP-CS-Fixer are similar tools

PHPCS seems to be more common. PHP Code Beautifier (PHPCBF) fixes the file after a defined rule set. PHP_CodeSniffer just notifies.

1. Install globally via Composer


2. Set Paths

For Sublime Text phpcs extension, set the paths to the *.bat

    // phpcbf settings

    // Path to where you have the phpcbf installed
    "phpcbf_executable_path": "E:/xampp/Composer/vendor/bin/phpcbf.bat",

    "phpcbf_additional_args": {
        //"--standard": "Squiz",
        "-n": "",
        "-s": "",
        //"--standard": "E:/xampp/phpcs/ruleset.xml"
        "--standard": "E:/xampp/phpcs/MyPSR12/ruleset.xml"

3. check installed path for rules:

E:\WEB>E:/xampp/Composer/vendor/bin/phpcs.bat --config-show

E:\WEB>E:/xampp/Composer/vendor/bin/phpcs.bat --config-set installed_paths E:\xampp\phpcs
Using config file: E:\xampp\Composer\vendor\squizlabs\php_codesniffer\CodeSniffer.conf

Config value "installed_paths" updated successfully; old value was "/e/xampp/phpcs/"

E:\WEB>E:/xampp/Composer/vendor/bin/phpcs.bat --config-show
Using config file: E:\xampp\Composer\vendor\squizlabs\php_codesniffer\CodeSniffer.conf

installed_paths: E:\xampp\phpcs

E:\WEB>E:/xampp/Composer/vendor/bin/phpcbf.bat E:\WEB\site\models\netzwerk.php --standard=MyPSR2 -n

💡 To debug rulesets and find out what to exclude run:

phpcs temp.php --standard=Generic,Squiz,PEAR,PSR2,Zend -s

via https://stackoverflow.com/a/44937114/814031

5. Rule sets

Ref: https://github.com/squizlabs/PHP_CodeSniffer/wiki

I want tabs and some formatting from MySource. This seems to be ok:

<?xml version="1.0"?>
<ruleset name="MyPSR12">
    <description>PSR12 with tabs instead of spaces.</description>
    <arg name="tab-width" value="4"/>
    <rule ref="MySource">
        <exclude name="Generic.WhiteSpace.DisallowTabIndent"/>

    <rule ref="Generic.WhiteSpace.DisallowSpaceIndent"/>
    <rule ref="Generic.WhiteSpace.ScopeIndent">
            <property name="indent" value="4"/>
            <property name="tabIndent" value="true"/>

    <rule ref="Squiz">
        <exclude name="Squiz.Commenting.LongConditionClosingComment"/>
        <exclude name="Squiz.Commenting.ClosingDeclarationComment.Missing"/>


Slows everything down. If not needed, disable it in main php.ini!

#zend_extension = "php_xdebug-2.9.6-7.3-vc15.dll"

Xdebug Issue "End of script output before headers: php-cgi.exe"

Server error! The server encountered an internal error and was unable to complete your > request.

Error message: End of script output before headers: php-cgi.exe

If you think this is a server error, please contact the webmaster.

Error 500 local.tools.marcus-obst.de Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8

This is caused when using a number as key of a query string like info.php?0=foo - which is weird.


ImageMagick Extension

Install he right version from here: https://mlocati.github.io/articles/php-windows-imagick.html (w32 Thread safe)

Once you downloaded the correct files:

  1. Extract from php_imagick-….zip the php_imagick.dll file, and save it to the ext directory of your PHP installation
  2. Extract from ImageMagick-….zip the DLL files located in the bin folder that start with CORE_RL or IM_MOD_RL, and save them to the PHP root directory (where you have php.exe), or to a directory in your PATH variable
  3. Add this line to your php.ini file: extension=php_imagick.dll
  4. Restart the Apache/NGINX Windows service (if applicable)

Polyfill ext-imagick

CGI Mode and php.ini

  • add custom .user.ini to project root to overwrite certain settings

track_errors = On


Error 500: malformed header from script. Bad header=<br>

If your webserver throws a HTML 500 error and Apache error_log file shows "malformed header from script. Bad header=
", then you probably have a misconfigured php.ini file in your website root directory.

Missing Authorization Header in PHP running in CGI-Mode

Edit httpd-xampp.conf add:

# PHP-CGI setup

SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0

ScriptAlias /php74/ "E:/xampp/php749/"





Error 503 in /examples folder

http://localhost/examples results in Error 503 because of Tomcat being installed

http://localhost/somethingelse/examples works


To fix it navigate to file: C:\xampp\apache\conf\extra\httpd-ajp.conf and comment out the following line with #: ProxyPass /examples ajp:// smax=0 ttl=60 retry=5 Then restart the server and enjoy these great Examples.