Docker Laravel Dev Environment

This post documents building a local Laravel development environment with Docker. Included are examples for debugging Laravel’s PHP with Xdebug using the Visual Studio Code editor. Source Code available on GitHub.

Install Laravel

In this example, we will be using the Composer Dependency Manager for PHP to install Laravel. To check if Composer is installed globally and in your PATH, enter composer --version in the CLI, for example,

composer --version
Composer version 1.4.1 2017-03-10 09:29:45

Get Composer if you need to install it.

With Composer, use the create-project command and the laravel/laravel package name followed by the directory to create the project in. The optional third argument is for a version number. For example, to install Laravel version 5.5 into a local directory such as home/laravel/mysite on the host computer, open a CLI then execute the composer create-project command from the directory where you want to create your project. For example:

cd ~/laravel

composer create-project laravel/laravel mysite "5.5.*"

Docker

Once composer is finished creating the Laravel project, we are ready to create the docker containers to host it. The following examples require both Docker and Docker Compose. Head on over to Docker, select Get Docker and the platform of your choice to install Docker on your computer. This should install both Docker and Docker Compose. The examples in this post were written while using Docker Community Edition, Version 17.09.0-ce.

Start Docker as needed and test the installation. Open a CLI and issue these commands.

docker --version

docker-compose --version

For the Windows platform with IIS installed, if port 80 is conflicting, shut down the IIS web server. Open an admin command prompt and enter net stop was

PHP Container

This dockerfile builds from the official Docker Hub PHP image. At the time of this writing, php 7.1.10 was the latest non RC version available in the library. If you want to use a different version, change the php version as needed, such as FROM php:5.6.31-fpm.

Create the app.dockerfile in the root of the laravel project. For example, home/laravel/mysite/app.dockerfile

app.dockerfile
FROM php:7.1.10-fpm

# php-fpm default WORKDIR is /var/www/html
# change it to /var/www
WORKDIR /var/www

RUN apt-get update && apt-get install -y \
    libmcrypt-dev \
    mysql-client --no-install-recommends \
    && docker-php-ext-install mcrypt pdo_mysql \
    && pecl install xdebug \
    && echo "zend_extension=$(find /usr/local/lib/php/extensions/ -name xdebug.so)\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_enable=1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_autostart=1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_connect_back=0\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_host=10.0.75.1\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.remote_port=9001\n" >> /usr/local/etc/php/conf.d/xdebug.ini \
    && echo "xdebug.idekey=REMOTE\n" >> /usr/local/etc/php/conf.d/xdebug.ini

For Mac platforms, change the app.dockerfile xdebug remote host IP, e.g., xdebug.remote_host=10.254.254.254

Then create an alias for IP 10.254.254.254 to your existing subnet mask as follows:

sudo ifconfig en0 alias 10.254.254.254 255.255.255.0

If you want to remove the alias, use sudo ifconfig en0 -alias 10.254.254.254

Nginx Container

Create a nginx server block configuration file for php and the Laravel project root.

nginx.conf
server {
    listen 80;
    index index.php index.html;
    root /var/www/public;

    location / {
        try_files $uri /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

This dockerfile builds from the official Docker Hub nginx image adding the nginx.conf Nginx server block configuration file to /etc/nginx/conf.d on the container.

web.dockerfile
FROM nginx:1.12

ADD nginx.conf /etc/nginx/conf.d/default.conf

The next page covers creating the docker-compose file, container build, create, start and stop. Xdebug and launch config for VS Code PHP debugging.


Remote Device Dev Environment Access

There are various ways to connect a mobile device to the computer running the local development instance of your web application for testing and debugging. A proxy server and/or USB cable are popular options.

Proxy Server

This method is pretty reliable and does not limit you to using browser specific settings on either the host or remote. The host computer will need HTTP proxy server software installed for proxying requests and responses. Fiddler and Charles are popular choices for this.

Both listen on port :8888 by default.

Fiddler - Tools - Options Screen
Fiddler – Tools – Options
Charles - Proxy - Proxy Settings
Charles – Proxy – Proxy Settings

Requirements – HTTP proxy server application software installed on the host computer.

Lookup the IPv4 local IP address of the dev environment host computer.

Command prompt

ipconfig /all

Terminal

# wifi
ipconfig getifaddr en1

# list 
ifconfig | grep inet

  • On a Mac, a quick way to get the local IP is by holding down the option key while clicking on the network icon in the system tray.

Device Settings

On the mobile device connected to the same Wi-Fi network as the host, modify the connection by entering the proxy server port and host computer local IP address.

Under Settings – Wi-Fi, long-press the connected network SSID, select modify network. Under the advanced options | proxy, select manual. For the Proxy hostname enter the host local IP address. Enter the proxy server port number and save.

Device - Network - Proxy Settings
Device – Network – Proxy Settings

Direct Connection with USB Cable

If you’re using Google’s Chrome browser on both the device and host. This resource covers how to use the browser developer tools to test and debug using a USB cable. I like using this method along with one of the proxy servers above – Dev Tools – Access Local Servers.

Requirements – Chrome 32 or later and USB drivers installed on the host computer. For Windows, check for the correct driver using Device Manager. Android 4.0 or later running Chrome for Android on the device with USB debugging enabled.

Resources

Docker Drupal Dev Environment

This post documents mounting a new Drupal Composer
project as a volume in a Docker container. Features include Drush, Drupal Console, mailhog and phpMyAdmin. A Docker-sync configuration is available for OS X.

Composer

With the release of Drupal 8.3, using Composer to manage Drupal projects has vastly improved and is becoming a best practice. See Getting Started at getcomposer.org to install Composer.

Drupal Composer Template

Using Composer and the Composer template for Drupal projects to create a new Drupal site.

Open a CLI and execute the composer create-project command from the directory where you want to create your project. For example,

composer create-project drupal-composer/drupal-project:8.x-dev mysitefolder --stability dev --no-interaction

Docker

The docker-compose.yml file from Docker4Drupal image stack is optimized for local development. Use curl to download the file into the root of your Drupal project. For example,

PowerShell

curl https://raw.githubusercontent.com/wodby/docker4drupal/master/docker-compose.yml -Outfile mysitefolder\docker-compose.yml

Bash

curl -o mysitefolder/docker-compose.yml  https://raw.githubusercontent.com/wodby/docker4drupal/master/docker-compose.yml

Update the docker-compose.yml file. Create a named volume for data persistence in the mariadb node.

docker-compose.yml
...
services:
  mariadb:
    ...
    volumes:
      - mysql:/var/lib/mysql

  • Note that an ellipsis … in the code snippets are not a part of the code and are there only to denote code that is being skipped and not applicable to the example. View all of the docker-compose.yml updates on GitHub.

In the php node, comment out the vanilla Drupal image node and uncomment the image without Drupal. Additionally, change the volume to mount the relative local directory ./ to /var/www/html in the container.

...

  php:
    image: wodby/drupal-php:7.1-2.1.0
    ...
    volumes:
      - ./:/var/www/html

In the nginx node, change the volume to mount the relative local directory ./ to /var/www/html in the container.

...

  nginx:
    ...
    volumes:
      - ./:/var/www/html

For data persistence, in the volumes node at the bottom of the docker-compose.yml file, replace the unused codebase volume with mysql.

...

  volumes:
    mysql:

Run containers

From the site folder, e.g., mysitefolder, execute docker-compose

docker-compose up -d

Drupal Console

Test drive Drupal Console by connecting to the php container.

docker-compose exec --user=82 php sh

List all of the Drupal Console commands.
Disconnect from the session with Ctrl+D

drupal list

Source Code

Resources

Acquia Certified Site Builder – Drupal 8

Earlier this week I took and passed the Acquia Certified Site Builder exam for Drupal 8. Here are some tips and a few resources that helped me prepare for this 50 question, 75 minute exam.

Acquia Academy

These free Drupal courses for the community were a big help. It has been several years since I have done any site building with Drupal and it was like going back to my home town, barely recognizable. The exam questions are mostly scenario based and the Building a Basic Site Using Drupal 8 course covers some of these. The videos also walk through the various administration menus that you will need to be familiar with.

wadmiraal/drupal

For the Acquia Academy Site Builder course, I used the Docker image created by wadmiraal to run Drupal locally. It was also handy for experimentation since the Drupal site could easily be reset to that of a fresh install. If you are already familiar with Docker, then this won’t add much to your plate on top of preparing for the Site Builder exam. Otherwise, I recommend sticking with the Drupal environment solutions covered in the courses. Another bonus is that this image comes with Devel and Drush already setup. Drupal Console is also ready for installation when advancing to the developer track.

Keys

Here are few tips and scenarios to consider when studying.

  • Understand how to create custom content types and how to modify their display.
  • Understand Taxonomy use for content categorization.
  • Understand how to create content relationships. There are good examples using Events and Sponsors in the Acquia Academy Site Builder course to demonstrate this.
  • Understand how to use blocks in the page layout. For example, using blocks to reuse header and footer content assets in various ways.
  • Understand Users, Roles and Permissions and how to change what a certain group of users can do in terms of content workflow.
  • Understand how to handle a compromised User account.
  • Understand how to handle comment spam.
  • Understand how to administer the core cache modules, including views data caching that is tucked away under the Advanced Views settings.
  • Understand the various Display modes.
  • Understand how to create pages and blocks with Views.
  • Understand best practices when communicating with the Drupal community. For example, when an issue is encountered with a module.
  • The Site Builder course at Acquia Academy uses a couple of contributed modules. The Devel module is used to generate a bunch of content nodes to demonstrate views and filters, etc. Pathauto is used to demonstrate generation of path aliases. Token and Ctools are required by Pathauto, so they also need to be installed. It is useful to know where to find and how to install modules. However, if a given scenario can be solved using core modules, that answer is preferred.
Acquia Certified Site Builder - Drupal 8 Badge

Other Resources

Docker WordPress Dev Environment

This post documents setting up local development environments with Docker using the official WordPress Docker repository as a base image. Page two includes configurations for remote PHP debugging with Xdebug and VS Code.

Docker Compose

Aside from making the container configuration easier to understand, Docker Compose coordinates the creation, start and stop of the containers used together in the environment.

The environment has multiple sites served on various ports including at least one WordPress site, a phpmyadmin site and mysql. For virtual host names, nginx-proxy is being used for containers with virtual host environment properties set in their docker compose data. Additionally, the mysql db container is accessible from the host at 127.0.0.1:8001

Create this docker compose yaml file in the projects root directory with the nginx-proxy configuration.

nginx-proxy.yml
version: "2"
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

Create this docker compose yaml file for the WordPress stack. This includes the linked MariaDB database and phpMyAdmin containers from their official repositories. Xdebug is not included in the official WordPress image on Docker Hub and will not be included in this configuration since it is using unmodified images. Adding xdebug and rebuilding the image is covered on page two.

wp.yml
version: "2"
services:
  db:
    image: mariadb
    volumes:
      - mysql:/var/lib/mysql
    ports:
      - "8001:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=secret
  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - "8002:80"
    links:
      - db:mysql
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - VIRTUAL_HOST=phpmyadmin.app
      - VIRTUAL_PORT=8002
  wp:
    image: wordpress
    volumes:
      - ./wordpress:/var/www/html
    ports:
      - "8003:80"
    links:
      - db:mysql
    environment:
      - WORDPRESS_DB_PASSWORD=secret
      - VIRTUAL_HOST=wordpress.dev
      - VIRTUAL_PORT=8003
volumes:
  mysql:

Update your systems hosts file.

hosts
# Docker (nginx-proxy)
127.0.0.1 phpmyadmin.app
127.0.0.1 wordpress.dev

Navigate to your project root in your CLI, such as Terminal on OS X, PowersShell or Cygwin on Windows.

Create the Containers

Create new nginx-proxy and WordPress containers using the up command with docker-compose.

docker-compose -f nginx-proxy.yml -f wp.yml up
  • The -f flags specify the compose files to use. Multiple compose files are combined into a single configuration. This multiple file solution is for demonstration purposes. Here is a single file example that can be run without a file flag.

Stop Containers

Stop the containers without removing them.

docker-compose -f wp.yml -f nginx-proxy.yml stop

Start Containers

Start the stopped containers. Include the nginx-proxy.yml first so when the WordPress containers are started the virtual hosts can be dynamically configured.

docker-compose -f nginx-proxy.yml -f wp.yml -f start
  • If you have restarted your computer and another process is using the nginx-proxy port, e.g., 80, you will need to halt that process before starting the container.

Shutdown Containers

Shutdown the environment using the down command. If your data is not stored in a volume, it will not persist since this will remove the containers.

docker-compose -f wp.yml -f nginx-proxy.yml down

The next page covers adding Xdebug and configuring VS Code for remote debugging.


Google Maps API with Webpack

Google Map application that uses a draggable location marker to set address, longitude and latitude geocode inputs. This post covers some of the Webpack tools for both a local development environment and production build of the app constructed of a few JavaScript modules and css.

For application details, I encourage you to read my Google Maps API with Browserify post from a year ago. The following overview highlights building the refactored code with Webpack. This refactor brings the application more up to date with ES6 standards. Other changes inlcude the removal of jQuery as a dependency. All of the source code is available for download and browsing at GitHub.

NPM

Here is the updated package.json for installing webpack, loaders and other dependencies. This file also defines scripts to start and run commands.

package.json
{
  "name": "gmap-webpack",
  "version": "0.0.1",
  "description": "location inputs populated by google maps api, built with webpack",
  "main": "",
  "scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --progress --inline --open",
    "build": "cross-env NODE_ENV=production webpack"
  },
  "devDependencies": {
    "babel-core": "^6.0.0",
    "babel-loader": "^6.0.0",
    "babel-preset-es2015": "^6.0.0",
    "cross-env": "^3.2.3",
    "css-loader": "^0.25.0",
    "style-loader": "^0.13.2",
    "webpack": "^2.2.1",
    "webpack-dev-server": "^2.4.1"
  }
}

The npm start command is using the cross-env plugin to set the Node environment variable properly for the platform. The webpack-dev-server then bundles the modules and launches a static web server inline for live reloading.

The npm run build command uses cross-env to set the environment flag. Then webpack bundles the modules according to the webpack.config below optimized with a source-map for production.

webpack.config
const path = require('path')
const webpack = require('webpack')

module.exports = {
    context: path.resolve(__dirname, './src'),
    entry: {
        app: './js/index.js',
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'bundle.js'
    },
    devtool: '#eval-source-map',
    module: {
        rules: [{
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/
        },
        {
            test: /\.css$/,
            use: ['style-loader','css-loader']
        }]
    }
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}

In the webpack.config file, the output.publicPath property is used by the webpack-dev-server to determine where the bundles should be served from.

The devtool property controls if and how source maps are generated. eval-source-map is for faster rebuild when in development. In production mode, devtool is set to use the appropriate source-map style instead.

To test drive the app and experiment with the dev server and live reloading, follow these steps assuming you have Node.js installed.

  1. Download and extract the source code or use git to clone the uiCookbook repository from https://github.com/jimfrenette/uiCookbook
  2. Navigate to the uiCookbook/geocode/gmap-webpack folder in your CLI, such as terminal or Cygwin.
  3. Run npm i or npm install
  4. Run npm start to bring up the dev server, bundle the modules and load the app in the browser.

Source Code


A Vue.js version of the Google Maps applicaton is available in the geocode/gmap-vue directory. The vue-cli webpack-simple scaffold was used to generate the Vue.js 2 project template.

Node.js

A collection of useful Node.js code snippets and resources.

HTTP Server

An easy way to get a Node.js http server up and running is by installing the http-server node module globally, npm install http-server -g. Here is an example using the -a option to specify an address to use. For more options, run http-server --help.

http-server -a localhost
  • http-server default is to use address 0.0.0.0 which is reachable by all IP addresses of the local machine’s network interface. Therefore, it may not be safe to run this address on a public network.

Shell Scripting

A collection of shell scripting resources.

Shell scripts are plain text files with an .sh extension.

If permission denied, the shell script may just need to be made executable. Example: for a script named backup.sh, using chmod u+x backup.sh will grant only the owner of the file execution permissions. For all users, replace the u with an a. You could also use chmod +x backup.sh which is the same as chmod a+x backup.sh.

chmod u+x backup.sh

Then try running the script again.

./backup.sh

rsync

This OS X example uses rsync to backup the Documents folder on the hard drive to an existing Documents folder on a Lexar USB stick. The --delete-before option deletes files from the target that do not exist in the source before rsync.

if [ -d "/Volumes/Lexar/Documents" ];
then
    rsync -avP "/Volumes/Macintosh HD/Users/Woz/Documents/" "/Volumes/Lexar/Documents/" --delete-before
fi

Use the --exclude option to prevent specific files and folders from being synced. This example excludes .DS_Store files, .git and node_modules folders from the backup.

if [ -d "/Volumes/Lexar/Code" ];
then
    rsync -avP --exclude .DS_Store --exclude .git --exclude node_modules "/Volumes/Macintosh HD/Users/Dinesh/Code/" "/Volumes/Lexar/Code/"
fi

  • These shell script examples are written for Cygwin and /cygdrive/c is how the C: drive of Windows is accessed. For Cygwin, the rsync package would need to be installed. On OS X and Linux, rsync is usually included in the system install.

This example uses rsync to backup an existing file to a server share.

if [ -f /cygdrive/c/Users/Gilfoyle/Dropbox/Private/Hooli.xlsx ];
then
    rsync -avP /cygdrive/c/Users/Gilfoyle/Dropbox/Private/Hooli.xlsx //FILESERVER/share/Hooli.xlsx
fi

This example uses rsync to backup an existing Private Dropbox folder to a server share.

if [ -d /cygdrive/c/Users/Gilfoyle/Dropbox/Private ];
then
    rsync -avP /cygdrive/c/Users/Gilfoyle/Dropbox/Private/ //FILESERVER/share/
fi

tar

This example checks for the existing source directory and creates a compressed archive in the current directory with a datetime string included in the filename. The source directory and its contents will be added to the archive while exluding node_modules .git and sass.cache

if [ -d /cygdrive/c/Users/Dinesh/Code ];
then
    now=$(date +"%Y-%m-%dT%H%M%S")
    tar -zcvf code_$now.tar.gz --exclude .git --exclude node_modules --exclude sass.cache /cygdrive/c/Users/Dinesh/Code
fi

dos2unix

Find all files inside the current directory and execute dos2unix on each converting DOS format line breaks (CRLF) to Unix format (LF).

find . -type f -exec dos2unix {} \;

unix2dos

Find all files inside the current directory and execute unix2dos on each converting Unix format line breaks (LF) to DOS format (CRLF).

find . -type f -exec unix2dos {} \;

Resources

Windows PowerShell

A collection of Windows PowerShell resources

Customization / Modules

PowerShell modules can enhance functionality and the user interface. In this example, the profile is updated to use git over an SSH connection, readline settings for a bash like experience, custom color output and a custom prompt.

My Customized PowerShell

Execution Policy

This command sets the PowerShell execution policy.

If you encounter an error such as …

{script path} cannot be loaded.
The file {script path} is not digitally signed.
You cannot run this script on the current system.
For more information about running scripts and setting execution policy,
see about_Execution_Policies at
http://go.microsoft.com/fwlink/?LinkID=135170.

You can set a Bypass execution policy for the current session.

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Menu Example

This PowerShell script contains a menu to execute various tasks using functions.

UtilityMenuExample.ps1
function ListProcess
{
    Get-Process | Group-Object Company | Sort-Object Count -Descending
}

function ListEnvVars
{
    Get-ChildItem env:
}

function ListEventLog
{
    Get-EventLog -List
}

function Cleanup
{
    Write-Host "Delete files from $env:temp older than 24 hours"
    Get-ChildItem -path $env:temp | where {$_.Lastwritetime -lt (date).addhours(-24)} | remove-item
    <# Clear-RecycleBin #>
    $Shell = New-Object -ComObject Shell.Application
    $RecBin = $Shell.Namespace(0xA)
    $RecBin.Items() | %{Remove-Item $_.Path -Recurse -Confirm:$false}
}

function ShowMenu
{
     param (
           [string]$Title = 'Menu'
     )
     Write-Host "====== $env:USERPROFILE - $Title ======="

     Write-Host "1: List Running Processes"
     Write-Host "2: List Environment Variables"
     Write-Host "3: List Event Log"
     Write-Host "4: Clean Temp and Recycle Bin"
     Write-Host "q: quit"
}

do
{
     ShowMenu
     $input = Read-Host "Please make a selection"
     switch ($input)
     {
        '1' {
            Clear-Host
            ListProcess
        }
        '2' {
            Clear-Host
            ListEnvVars
        }
        '3' {
            Clear-Host
            ListEventLog
        }
        '4' {
            Clear-Host
            Cleanup
        }
        'q' {
            return
        }
     }
     pause
}
until ($input -eq 'q')

Compress-Archive

This example creates a zip file of the Documents folder with a datetime string in the zip filename.

Compress-Archive "$env:USERPROFILE\Documents" "$env:USERPROFILE\Documents_$(get-date -f yyyyMMdd'T'HHmmss).zip"

Resources

Cygwin Oh My ZSH Recipe

This post documents my Cygwin + Oh My ZSH configuration which gives me a consistent cross platform terminal experience on Windows, OS X and Linux.

Web development workflow with git, node, npm, docker, vagrant, etc. is more comfortable when using the same familiar bash shell interface across different operating systems.

I don't always use the command line in Windows - But when I do, I prefer Cygwin

Install Cygwin

I used these settings with the Cygwin for 64-bit versions of Windows.

    Cygwin Setup

  1. Choose A Download Source: Install from Internet
  2. Select Root Install Directory: C:\Cygwin
    Install For All Users
  3. Select Local Package Directory: C:\Users\{UserName}\AppData\Local\Cygwin
    note: if your %userprofile% contains spaces, for example: C:\Users\Jared Dunn, this may cause issues.
  4. Select Your Internet Connection: Direct Connection
  5. Choose A Download Site: http://mirrors.xmission.com
    This mirror was reliable for me. They should generally all be up to date. It is recommended that you select a mirror site that is closest to you. Visit the Cygwin Mirror Sites page for more information.
  6. Select Packages:
    • curl: Muti-protocol file transfer tool
    • fzf-zsh: fzf key bindings for Z shell
    • fzf-zsh-completion: fzf completion for Z shell
    • git: Distributed version control system
    • gitk: Git repository browser
    • zsh: The Z-Shell

Configure Cygwin Home Directory

Make your Cygwin home directory the same as your Windows User profile.

Edit the Cygwin /etc/nsswitch.conf file.

Add or edit the following line: db_home: /%H

more info: Stack Overflow – How can I change my Cygwin home folder after installation?

Install Oh My ZSH

Launch Cygwin and install Oh My ZSH using curl

sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

I received this output while Oh My ZSH install completed:

Looking for an existing zsh config...
Using the Oh My Zsh template file and adding it to ~/.zshrc
Copying your current PATH and adding it to the end of ~/.zshrc for you.
I can't change your shell automatically because this system does not have chsh.
Please manually change your default shell to zsh!

Edit the Cygwin /etc/nsswitch.conf file.

Add or edit the following line: db_shell: /bin/zsh

More info: Stack Overflow – Set Default Shell in Cygwin

Mintty Configuration

This section documents some additional personalization preferences. Access the options by selecting the Cygwin application icon in the Title bar. For Looks, I have medium transparency selected.

Cygwin - Options - Looks
Options – Looks

For text, I have the Fira Code fonts installed on my system so have elected to use them and since my display is UHD, I am using the retina version. I have also set the font smoothing to full which is equivalent to subpixel anti-aliasing (“ClearType”).

Cygwin - Options - Text
Options – Text

Lastly, I wanted to add some spacing between the window borders and command text. I discovered that this could be done by editing the mintty configuration file in my home directory. I added the following line to the file, Padding=16. The entire configuration file is shown below.

~/.minttyrc
BoldAsFont=-1
Columns=96
Rows=48
Transparency=medium
Padding=16
Font=Fira Code Retina
FontWeight=450
FontSmoothing=full

Test drive using this handy cal 2017 command to display a calendar.

cal 2017

Resources

Creating a Custom WordPress Page Template

I wanted a front-end page template to play around with the new REST API that is now in WordPress 4.7 core. A custom page template gives me the latitude I want at this stage of development, so here is how I did it.

Create the Template File

There are a couple of ways to do this.

Option 1

Clone and edit the existing page.php template. Open page.php in an editor and Save As… api-test.php. Remove the guts of the page containing the while loop for iterating through and displaying posts. For example, my clone of wp-content/themes/twentyseventeen/page.php saved as api-test.php looks like this:

api-test.php
<?php

get_header(); ?>

<div class="wrap">
    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">


        </main><!-- #main -->
    </div><!-- #primary -->
</div><!-- .wrap -->

<?php get_footer();

The original page meta data properties in the comment at the top have been removed.

Option 2

Start from scratch with a completely blank slate. Create a new php file in the root of your active theme with a single line of code to set the Template Name meta data. Now we have a new custom template that can selected for pages when editing.

api-test.php
<?php /* Template Name: api-test */ ?>

Option 3

This is nearly the same as option 1, only with addition of the meta data property to set the Template Name property like option 2.

Clone and edit the existing page.php template. Open page.php in an editor and Save As… api-test.php. Edit api-test.php replacing the meta data at the top with the Template Name property used in the blank slate option above. Then remove the guts of the page containing the while loop for iterating through and displaying posts. For example, my clone of wp-content/themes/twentyseventeen/page.php saved as api-test.php looks like this:

api-test.php
<?php /* Template Name: api-test */

get_header(); ?>

<div class="wrap">
    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">


        </main><!-- #main -->
    </div><!-- #primary -->
</div><!-- .wrap -->

<?php get_footer();

Create the Page Slug

Login into the dashboard and add a new page.

If using Option 1, update the permalink slug to match the page filename. For example, api-test

WordPress Page Permalink Slug
api-test page slug

Select the new template if using options 2 or 3.

WordPress Page Attributes - Custom Template
select api-test template

Resources

WordPress

Custom Dashboard

WordPress plugin stub to customize the dashboard for users without administrator access.

Source Code

Custom Homepage

Clone and edit the existing theme index.php template. Open index.php in an editor and Save As… home.php. This file will automatically take over the themes index.php, and it will be displayed as the homepage.


Development Environments

Docker

docker-compose.yml

Vagrant

Laravel Homestead
If you develop Laravel apps on Homestead, you can also install WordPress on the same Virtual Machine. This example Homestead.yaml file has a configuration for 4 different sites. Two Laravel sites, a WordPress site and a phpmyadmin site.

Vagrant configuration designed for development of WordPress plugins, themes, or websites.

http://vccw.cc/

What’s Included

Git Setup

To provision from the master branch,

git clone https://github.com/vccw-team/vccw.git

cd vccw

cp provision/default.yml site.yml

# edit php.ini settings in site.yml as needed

vagrant up

vccw.cc zip download link uses release branch.


XAMPP, MAMP, IIS


Development Resources

wp-config.php

REPLACE

define('WP_DEBUG', false);

WITH

// Enable WP_DEBUG mode
define( 'WP_DEBUG', true );

// Enable Debug logging to the /wp-content/debug.log file
define( 'WP_DEBUG_LOG', true );

// Disable display of errors and warnings 
define( 'WP_DEBUG_DISPLAY', false );
@ini_set( 'display_errors', 0 );

Log Your Debug Statements

// array or object variable
error_log(print_r($myvariable, true));

// string
error_log(print_r($mystring));

Vue.js CLI Webpack Laravel Proof of Concept

This post is a simple proof of concept for using vue-cli to scaffold a Webpack Laravel project. The production build modifies the default Laravel blade view and outputs the built assets into the public/static directory. The development workflow demonstrates hot reloading and css style extraction.

Environment

This example was created using a fresh install of Laravel 5.2 served by XAMMP on Windows 10.
Nodejs version 6.9.1
NPM version 3.10.8
vue-cli version 2.4.0
git version 2.7.0

Development

Change to the laravel directory. For example,

cd c:/xampp/htdocs/laravel

Use vue-cli to download vuejs, wepbpack and template dependencies and create the project in the laravel/resources folder.

vue init webpack resources

Change to the resources directory.

cd resources

Install node modules with npm install or npm i.

npm i

Run development server.

  • Check the dev.port in resources/config/index.js to make sure it is not the same as the port used by the XAMPP Apache server.
npm run dev

Update the /resources/src/components/Hello.vue vue commponent to see the hot reloading take place. In the script portion of the component, change the msg value. For example, ‘Welcome to Your Vue.js Laravel App’.

Hello.vue
<script>
export default {
  name: 'hello',
  data () {
    return {
      msg: 'Welcome to Your Vue.js Laravel App'
    }
  }
}
</script>
  • For development on Laravel sever generated pages, another solution is to update the package.json scripts property so the npm run dev command runs webpack development compilation with watch mode enabled. Watch mode recompiles after every change is saved.
package.json
"dev": "cross-env NODE_ENV=development webpack --watch --progress --colors"

Production

Edit /resources/config/index.js. Update the build paths so the welcome.blade.php view gets the index markup and the assets are built to the public/static folder.

index.js
build: {
    ...
    index: path.resolve(__dirname, '../views/welcome.blade.php'),
    assetsRoot: path.resolve(__dirname, '../../public'),
  • The ellipsis … in the snippet above is not a part of the code and is there only to denote lines that are skipped and not applicable to the example.

Run production build.

npm run build

Manually refresh the laravel.dev page served by XAMPP to verify the production build.

Resources

Laravel Install on Windows IIS

This post documents installing PHP 7, PHP Manager and Laravel 5.3 on Internet Information Services (IIS) Manager version 10 which ships with Windows 10 Pro. This Laravel Installation will also be configured to connect to a SQLite database.

Internet Information Services (IIS) Manager

IIS needs to be installed, open the Windows Features dialog to check the installation. One way to do this is by selecting the start button and type Windows Features to bring up a list where “Turn Windows features on or off” can be selected. Another way to get to this Control Panel app is Windows + R key combination and run appwiz.cpl. Turn Windows features on or off link should be in the upper left panel.

Windows Features - IIS and .NET Framework 3.5 enabled
Windows Features

Notice that under .NET Framework 3.5 that both Windows Communication Foundation features are enabled. This may be needed in order for a successful install of PHP Manager. Installation of PHP Manager for IIS requires .NET 3.5 to work properly.

PHP and PHP Manager

Install PHP 7 for Windows using the Microsoft Web Platform Installer. It’s an easy way to get both PHP and the PHP Manager installed and takes some of the guess work out of getting PHP up and running on Windows and IIS.

  • If PHP Manager Fails to install after confirming that .NET 3.5 is installed and enabled, this is likely due to the installer throwing an error when checking the IIS version. Change the W3SVC MajorVersion value to 7 (decimal) in the registry. After the install is completed, change the value back to 10. More info is available at the PHP Manager – Refuses to install for WTP10 view issue page.
Registry Editor - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters]

* YMMV, I am not responsible if you hose your system when editing the registry.

PHP Extensions

In the Internet Information Services (IIS) Manager, Open PHP Manager and select the Enable or disable an extension link. Ensure that the extensions needed for Laravel are enabled. If you want to be able to connect to a SQLite database, enable the php_sqlite3.dll PHP extension. The figure below shows the PHP extensions I have enabled for a working Laravel 5 installation.

PHP Extensions provide additional functionality to PHP runtime, such as database connectivity, caching, debugging and others. Use this feature to enable or disable PHP extensions and to configure PHP extensions settings.
PHP Manager – PHP Extensions

Composer for Windows

Download and run Composer-Setup.exe Windows Installer. The installer will download Composer and set up the PATH environment variable.

Using the Windows Command Prompt, make sure Composer for Windows is installed by running the composer –version command.

composer --version

Git for Windows

Git for Windows is needed so Composer for Windows can download packages. During the installation process, Adjusting your PATH environment, make sure the option, Use Git from the Windows Command Prompt is enabled.

Using the Windows Command Prompt, make sure Git for Windows is installed by running the git –version command.

git --version

Create Laravel Project

Bring up the Adminstrator Command Prompt. One way to do this is by selecting the start button and type command to bring up a list where “Command Prompt” can be selected. Right click on Command Prompt and select Run as administrator
.

cd c:/intepub

composer create-project laravel/laravel laravel "5.3.*"

Add Website

Open Internet Information Services (IIS) Manager. Right click on the server and select Add Website. Fill out the form as follows:
Site name: Laravel
Application pool: DefaultAppPool
Physical path: C:\inetpub\laravel\public
Host name: laravel.win

Select “Test Settings” and then “OK” if successful.

Add Website
IIS – Add Website

Hosts Mapping

Since the Host name laravel.win was entered for the website, the hosts file needs to be updated. Open Notepad as an administrator. One way to do this is by selecting the start button and type Notepad to bring up a list where it can be selected. Right click on Notepad and select Run as administrator.

Select File | Open, or Ctrl + O and change the File type from Text Documents (*.txt) to All Files (*.*). Browse to C:\Windows\System32\drivers\etc and select the hosts file. Add an entry to map localhost to laravel.win as follows.

hosts
127.0.0.1   localhost
127.0.0.1   laravel.win

Laravel Storage Permissions

In File Explorer, right click on the storage folder in C:\inetpub\laravel and select Properties. Under the Security tab, grant full control of the storage folder to IUSR as shown in the figure below.

Properties Dialog - Security
Permissions for storage
Laravel web.config

Since IIS does not have an .htaccess file like Apache, create a web.config file in C:\inetpub\laravel\public as follows. *

web.config
<configuration>
    <system.webServer>
        <defaultDocument>
            <files>
                <clear />
                <add value="index.php" />
                <add value="default.aspx" />
                <add value="Default.htm" />
                <add value="Default.asp" />
                <add value="index.htm" />
                <add value="index.html" />
            </files>
        </defaultDocument>
        <rewrite>
            <rules>
                <rule name="Imported Rule 1" stopProcessing="true">
                    <match url="^(.*)/$" ignoreCase="false" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Redirect" redirectType="Permanent" url="/{R:1}" />
                </rule>
                <rule name="Imported Rule 2" stopProcessing="true">
                    <match url="^" ignoreCase="false" />
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.php" />
                </rule>
            </rules>
        </rewrite>
        <httpErrors errorMode="Detailed" />
    </system.webServer>
</configuration>

* The rewrite rule definitions in the web.config require the URL Rewite 2.0 extension. For easy installation, use the Free Web Platform Installer.

Laravel SQLite Database

Create a SQLite database * in C:\inetpub\laravel\database. Edit C:\inetpub\laravel\.env database values to configure the database connection.

.env
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=database/laravel.sqlite
DB_USERNAME=
DB_PASSWORD=

* DB Browser for SQLite is an open source, freeware tool used to create, design and edit SQLite database files.

Restart IIS

In an Administrative Command Prompt, restart IIS so all of the changes get applied.

iisreset /restart

In IIS, make sure that the Larvel web site has been started. If the W3SVC service is not running, it can be started with the following command.

net start w3svc

Once all is said and done, load http://laravel.win in a web browser. Laravel 5.3 default page should look similar to the screen shot below.

Laravel 5.3 Default Screen
Laravel 5.3

Installing Laravel on XAMPP

This post documents how to install Laravel locally using XAMPP. When you are developing with Laravel on an older computer or one with limited resources, using XAMPP for your local PHP and MySQL development server works well on Windows.

Laravel PHP Requirements

This table shows the minimum PHP version requirements for the latest versions of Laravel.

Laravel PHP
5.3 5.6.4
5.1, 5.2 5.5.9
5.0 * 5.4

* PHP 7 not supported in this version

Head over to the Apache Freinds site to download the version of XAMPP that meets the minimum PHP requirements for the version of Laravel you are supporting. If you are developing in Laravel 5.2 currently and also want to be able to support Laravel 5.3, get the XAMPP version that includes PHP 7.

For Windows, I have xampp installed at the root directory on C:\ drive. here is the virtual hosts configuration I am using for Laravel. More information for setting up XAMPP on Windows.

httpd-vhosts.conf
<VirtualHost *:8080>
    DocumentRoot "C:/xampp/htdocs/laravel/public"
    ServerName laravel.dev
    ServerAlias www.laravel.dev
    SetEnv APPLICATION_ENV development
    <Directory "C:/xampp/htdocs/laravel">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

Edit C:\Windows\System32\drivers\etc\hosts file, mapping laravel.dev hostname to 127.0.0.1 localhost IP address.

hosts
127.0.0.1   laravel.dev

Install Laravel

This example will install laravel 5.2 into C:/xampp/htdocs/laravel using Composer. Use the create-project command and the laravel/laravel package name followed by the directory to create the project in. The optional third argument is for a version number.

cd c:/xampp/htdocs

composer create-project laravel/laravel laravel "5.2.*"

Slim 3 MVC

This post documents a Slim 3 PHP micro framework application with models, views and controllers (MVC) using Twig templates to display data from a database.

Requirements

* I am using XAMPP on Windows as my web server with URL rewriting.

Setup

Clone or download the source code from GitHub.

Source Code

After Composer had been installed, using a command shell, navigate to the directory where the source code has been cloned or extracted to. In the project root is a composer.json package file. This file defines the project requirements. Run the composer require command to install the project dependencies read from the composer.json package configuration into the project.

# install slim and it's dependencies
composer require slim/slim "^3.0"

The included composer.json package file also contains an autoload property for PSR-4 autoloading support in Composer. This will map the App namespace to the relative path specified for the classes.

composer.json
{
    "require": {
        "slim/slim": "^3.0",
        "slim/twig-view": "^2.1",
        "php-di/slim-bridge": "^1.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src\\classes"
        }
    }
}
# update and optimize the autoloader
composer dump-autoload -o

There are two versions of this starter application:

  1. tag oobdic – Out of the box dependency injection configuration, uses Slim’s built-in dependency container.
  2. tag phpdiPHP-DI/Slim-Bridge dependency injection into the controller. This is the latest version of the starter app.
  • To install PHP-DI/Slim-Bridge in an existing project, require php-di/slim-bridge using composer.
composer require php-di/slim-bridge

Changing the Database Connection String

The connection string is currently set to connect to the SQLite demo.db database which makes it easy to include a database with the project.

App.php

\App\Database\DatabaseInterface::class => function (ContainerInterface $container) {
    return new \App\Database\PDODatabase('sqlite:../data/demo.db');
}

Here is the container definition with the connection string changed for a MySQL database.


\App\Database\DatabaseInterface::class => function (ContainerInterface $container) {
    return new \App\Database\PDODatabase('mysql:host=localhost;dbname=demo', 'username', 'password');
}

Twenty Sixteen Sass

I recently created individual Sass modules for each of the WordPress Twenty Sixteen style.css stylesheet numbered and annotated sections. Additionally, the fonts and colors have been converted into Sass variables. All of this makes customizing this theme easier.

Source Code

There is also a gulpfile.js included with a task for compiling the .scss files into a new style.css. This task uses cssnano and the workflow described in this post, Sass Workflow Using cssnano and Autoprefixer.

  1. Install the files into the /wp-content/themes/twentysixteen folder.
  2. To recompile the Twenty Sixteen stylesheet style.css from the scss files, first run npm install to get the dependencies.

    # installs dependencies
    npm install
    
  3. Then run the gulp task that will compile them

    # compile into styleheet
    gulp css
    

Resources

Visual Studio Code

My favorite feature so far is in the form of an extension. The vscode-php-debug extension works better than any free PHP debugging solution I have tried, such as Eclipse, Netbeans or SublimeTextXdebug. In order to use the debugger, the Xdebug PHP extension will need to be installed for your version of PHP. Here are some Xdebug installation resources:

User Settings

My VS Code user preferences – I am not thrilled with the existing VS Code Git integration, I prefer the command line tools for this, so I have it disabled in my settings. Another setting I changed from the default, "workbench.editor.enablePreview": true – When this is set to true, a subsequent file open will close the previous file if it has not been double clicked or edited. I wanted to keep the files open, so I set this to false.

~\AppData\Roaming\Code\User\settings.json
// Place your settings in this file to overwrite the default settings
{
    "editor.renderWhitespace": "all",
    "editor.fontFamily": "Fira Code",
    "editor.fontLigatures": true,
    "editor.fontSize": 13,
    "editor.lineHeight": 22,
    "editor.minimap.enabled": false,

    "files.trimTrailingWhitespace": true,

    // Is git enabled
    "git.enabled": false,

    "window.zoomLevel": 0,

    "workbench.editor.enablePreview": false,
    "workbench.iconTheme": "vs-seti",
    "workbench.activityBar.visible": true,
    "workbench.sideBar.location": "left"
}

Extensions

Here is a list of my favorite VS Code extensions

PHP Debug

Remote debugging with xdebug – As noted in the Remote Host Debugging section, To make VS Code map the files on the server to the local machine, add the serverSourceRoot and localSourceRoot settings in the project .vscode/launch.json file. For example,

launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for XDebug",
            "type": "php",
            "request": "launch",
            "port": 9000,
            "serverSourceRoot": "/var/www/html",
            "localSourceRoot": "${workspaceRoot}"
        },
        {
            "name": "Launch currently open script",
            "type": "php",
            "request": "launch",
            "program": "${file}",
            "cwd": "${fileDirname}",
            "port": 9000
        }
    ]
}

Resources