Setting Up a Local WordPress Development Environment with Apache, PHP, MariaDB, and WP CLI on Mac OSX 10.11

Introduction

This guide will cover installing and configuring MariaDB (an open source drop-in replacement for MySQL), setting up Apache 2.4 (which is already installed on Mac OSX 10.11) along with PHP, and the installation of the WP CLI command line program for managing WordPress installations (including downloading, installing, configuring, enabling themes, plugins, etc.). These services in concert will allow us to host a local development environment for working on WordPress (and other) web projects.

Setting up Apache

Check if apache2 / httpd is already installed (it is on this version of Mac OSX 10.11):

alanturing /etc/apache2 $ whereis httpd
/usr/sbin/httpd
alanturing /etc/apache2 $ httpd -v
Server version: Apache/2.4.16 (Unix)
Server built:   Jul 31 2015 15:53:26
alanturing /etc/apache2 $ httpd -V
Server version: Apache/2.4.16 (Unix)
Server built:   Jul 31 2015 15:53:26
Server's Module Magic Number: 20120211:47
Server loaded:  APR 1.4.8, APR-UTIL 1.5.2
Compiled using: APR 1.4.8, APR-UTIL 1.5.2
Architecture:   64-bit
Server MPM:     prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_FLOCK_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/usr"
 -D SUEXEC_BIN="/usr/bin/suexec"
 -D DEFAULT_PIDLOG="/private/var/run/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="/private/etc/apache2/mime.types"
 -D SERVER_CONFIG_FILE="/private/etc/apache2/httpd.conf"

Control apache Service

Execute the 'httpd -H' command to view the command line options for interacting with Apache:

alanturing /Library/WebServer/Documents $ httpd -h
Usage: httpd [-D name] [-d directory] [-f file]
             [-C "directive"] [-c "directive"]
             [-k start|restart|graceful|graceful-stop|stop]
             [-v] [-V] [-h] [-l] [-L] [-t] [-T] [-S] [-X]
Options:
  -D name            : define a name for use in <IfDefine name> directives
  -d directory       : specify an alternate initial ServerRoot
  -f file            : specify an alternate ServerConfigFile
  -C "directive"     : process directive before reading config files
  -c "directive"     : process directive after reading config files
  -e level           : show startup errors of level (see LogLevel)
  -E file            : log startup errors to file
  -v                 : show version number
  -V                 : show compile settings
  -h                 : list available command line options (this page)
  -l                 : list compiled in modules
  -L                 : list available configuration directives
  -t -D DUMP_VHOSTS  : show parsed vhost settings
  -t -D DUMP_RUN_CFG : show parsed run settings
  -S                 : a synonym for -t -D DUMP_VHOSTS -D DUMP_RUN_CFG
  -t -D DUMP_MODULES : show all loaded modules
  -M                 : a synonym for -t -D DUMP_MODULES
  -t                 : run syntax check for config files
  -T                 : start without DocumentRoot(s) check
  -X                 : debug mode (only one worker, do not detach)

As we can see, we'll use the httpd -k command with one of the following parameters (start|restart|graceful|graceful-stop|stop) to control the daemon; for example:

alanturing /etc/apache2 $ sudo httpd -k graceful

This will gracefully restart the process and apply any new configuration changes.

Use httpd -S to take a look at the current virtualhosts and configuration:

alanturing ~ $ httpd -S
VirtualHost configuration:
*:80                   is a NameVirtualHost
         default server test.dev (/private/etc/apache2/extra/httpd-vhosts.conf:24)
         port 80 namevhost test.dev (/private/etc/apache2/extra/httpd-vhosts.conf:24)
                 alias www.test.dev
ServerRoot: "/usr"
Main DocumentRoot: "/Library/WebServer/Documents"
Main ErrorLog: "/private/var/log/apache2/error_log"
Mutex proxy-balancer-shm: using_defaults
Mutex rewrite-map: using_defaults
Mutex proxy: using_defaults
Mutex default: dir="/private/var/run/" mechanism=default
Mutex mpm-accept: using_defaults
PidFile: "/private/var/run/httpd.pid"
Define: DUMP_VHOSTS
Define: DUMP_RUN_CFG
User: name="_www" id=70 not_used
Group: name="_www" id=70 not_used

In the output of the 'httpd -S' command we can see that the DocumentRoot is set to the default location '/Library/WebServer/Documents'; change it if you wish. We can also see that I've configured a couple of Virtual Hosts which I have mapped for local access in '/etc/hosts'. I chose to leave the 'User' and 'Group' settings to the default _www as well.

Verify Syntax of Apache Config Files

After making changes to the '/etc/apache2/httpd.conf' file, or any of the configuration files included from the main config (such as the '/etc/apache2/extra/httpd-vhosts.conf' file where the Virtual Hosts are defined) it is good practice to run the 'httpd -t' command to check the syntax of the configuration files:

alanturing /Library/WebServer/Documents $ httpd -t
Syntax OK

Setup Virtual Hosts

Here is an example Virtual Host configuration with an example of custom logging directives. This and other Virtual Host directives are to be placed in the '/etc/apache2/extra/httpd-vhosts.conf'.

<VirtualHost *:80>

  ServerName test.dev
  ServerAlias www.test.dev
  DocumentRoot "/Library/WebServer/Documents/test/"

  LogLevel warn
  ErrorLog "/Library/WebServer/logs/test-dev.log"
  CustomLog "/Library/WebServer/logs/test-dev-debug.log" combined

  <Directory /Library/WebServer/Documents/test/>
    AllowOverride All
    Require all granted
  </Directory>

</VirtualHost>

Here is an example '/etc/hosts' file which maps local requests for 'test.dev' to localhost to be handled by the above Virtual Host:

##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1    localhost

127.0.0.1    test.dev www.test.dev

255.255.255.255    broadcasthost
::1             localhost

Installing and Configuring MariaDB

In order to prepare a local database we'll begin by installing MariaDB, which is an open source replacement for MySQL. This guide assumes Homebrew has already been installed. Homebrew is 'the missing package manager for macOS'; find more information and installation instructions here. I started with a 'brew update' in order to make sure my homebrew installation was up to date:

RYANs-iMac-6:~ alanturing$ brew update
Updated Homebrew from ff9aa97 to eb4538c.
==> Migrating Homebrew to v0.9.9
remote: Counting objects: 993, done.
remote: Compressing objects: 100% (853/853), done.
remote: Total 993 (delta 245), reused 522 (delta 117), pack-reused 0
Receiving objects: 100% (993/993), 902.13 KiB | 0 bytes/s, done.
Resolving deltas: 100% (245/245), completed with 156 local objects.
From https://github.com/Homebrew/brew
 + eb4538c...41b2df8 master     -> origin/master  (forced update)
HEAD is now at 41b2df8 Merge pull request #1177 from scpeters/man_exit_code
==> Homebrew has enabled anonymous aggregate user behaviour analytics
Read the analytics documentation (and how to opt-out) here:
  https://git.io/brew-analytics
==> Tapping homebrew/core
Cloning into '/usr/local/Library/Taps/homebrew/homebrew-core'...
remote: Counting objects: 3725, done.
remote: Compressing objects: 100% (3616/3616), done.
remote: Total 3725 (delta 10), reused 631 (delta 1), pack-reused 0
Receiving objects: 100% (3725/3725), 2.98 MiB | 0 bytes/s, done.
Resolving deltas: 100% (10/10), done.
Checking connectivity... done.
Tapped 3604 formulae (3,752 files, 9.2M)
==> Cleaning up /Library/Caches/Homebrew...
Removing: /Library/Caches/Homebrew/cscope-15.8a.el_capitan.bottle.tar.gz... (177.7K)
Removing: /Library/Caches/Homebrew/macvim-7.4-89.tar.gz... (17.4M)
Removing: /Library/Caches/Homebrew/putty-0.66.el_capitan.bottle.tar.gz... (761.6K)
Removing: /Library/Caches/Homebrew/vim-7.4.1063.tar.gz... (11.8M)
==> Migrating /Library/Caches/Homebrew to /Users/alanturing/Library/Caches/Homebrew...
==> Deleting /Library/Caches/Homebrew...
Already up-to-date.
==> Migrating HOMEBREW_REPOSITORY (please wait)...
==> Migrated HOMEBREW_REPOSITORY to /usr/local/Homebrew!
Homebrew no longer needs to have ownership of /usr/local. If you wish you can
return /usr/local to its default ownership with:
  sudo chown root:wheel /usr/local

Now that homebrew has been updated, we'll go ahead and run the initial homebrew command to install MariaDB:

RYANs-iMac-6:~ alanturing$ brew install mariadb
==> Installing dependencies for mariadb: openssl
==> Installing mariadb dependency: openssl
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2j.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring openssl-1.0.2j.el_capitan.bottle.tar.gz
==> Using the sandbox
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

This formula is keg-only, which means it was not symlinked into /usr/local.

Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries

Generally there are no consequences of this for you. If you build your
own software and it requires this formula, you'll need to add to your
build variables:

    LDFLAGS:  -L/usr/local/opt/openssl/lib
    CPPFLAGS: -I/usr/local/opt/openssl/include

==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2j: 1,695 files, 12M
==> Installing mariadb
==> Downloading https://homebrew.bintray.com/bottles/mariadb-10.1.17.el_capitan.bottle.tar.gz
######################################################################## 100.0%
==> Pouring mariadb-10.1.17.el_capitan.bottle.tar.gz
==> /usr/local/Cellar/mariadb/10.1.17/bin/mysql_install_db --verbose --user=alanturing --basedir=/usr/local/Cellar/mariadb/10.1.17 --datadir=/usr/local/var/mysql --tmpdir=/tmp
==> Caveats
A "/etc/my.cnf" from another install may interfere with a Homebrew-built
server starting up correctly.

To connect:
    mysql -uroot

To have launchd start mariadb now and restart at login:
  brew services start mariadb
Or, if you don't want/need a background service you can just run:
  mysql.server start
==> Summary
🍺  /usr/local/Cellar/mariadb/10.1.17: 573 files, 137M
RYANs-iMac-6:~ alanturing$
RYANs-iMac-6:~ alanturing$ unset TMPDIR
RYANs-iMac-6:~ alanturing$ mysql_install_db
Installing MariaDB/MySQL system tables in '/usr/local/var/mysql' ...
2016-09-30 11:21:25 140735226253312 [Note] /usr/local/Cellar/mariadb/10.1.17/bin/mysqld (mysqld 10.1.17-MariaDB) starting as process 38941 ...
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Using mutexes to ref count buffer pool pages
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: The InnoDB memory heap is disabled
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Compressed tables use zlib 1.2.5
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Using SSE crc32 instructions
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Initializing buffer pool, size = 128.0M
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Completed initialization of buffer pool
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Highest supported file format is Barracuda.
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: 128 rollback segment(s) are active.
2016-09-30 11:21:25 140735226253312 [Note] InnoDB: Waiting for purge to start
2016-09-30 11:21:25 140735226253312 [Note] InnoDB:  Percona XtraDB (http://www.percona.com) 5.6.31-77.0 started; log sequence number 1616819
2016-09-30 11:21:25 123145313034240 [Note] InnoDB: Dumping buffer pool(s) not yet started
OK
Filling help tables...
2016-09-30 11:21:28 140735226253312 [Note] /usr/local/Cellar/mariadb/10.1.17/bin/mysqld (mysqld 10.1.17-MariaDB) starting as process 38945 ...
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Using mutexes to ref count buffer pool pages
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: The InnoDB memory heap is disabled
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Compressed tables use zlib 1.2.5
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Using SSE crc32 instructions
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Initializing buffer pool, size = 128.0M
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Completed initialization of buffer pool
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Highest supported file format is Barracuda.
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: 128 rollback segment(s) are active.
2016-09-30 11:21:28 140735226253312 [Note] InnoDB: Waiting for purge to start
2016-09-30 11:21:28 140735226253312 [Note] InnoDB:  Percona XtraDB (http://www.percona.com) 5.6.31-77.0 started; log sequence number 1616829
2016-09-30 11:21:28 123145313034240 [Note] InnoDB: Dumping buffer pool(s) not yet started
OK
Creating OpenGIS required SP-s...
2016-09-30 11:21:30 140735226253312 [Note] /usr/local/Cellar/mariadb/10.1.17/bin/mysqld (mysqld 10.1.17-MariaDB) starting as process 38949 ...
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Using mutexes to ref count buffer pool pages
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: The InnoDB memory heap is disabled
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Compressed tables use zlib 1.2.5
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Using SSE crc32 instructions
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Initializing buffer pool, size = 128.0M
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Completed initialization of buffer pool
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Highest supported file format is Barracuda.
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: 128 rollback segment(s) are active.
2016-09-30 11:21:31 140735226253312 [Note] InnoDB: Waiting for purge to start
2016-09-30 11:21:31 140735226253312 [Note] InnoDB:  Percona XtraDB (http://www.percona.com) 5.6.31-77.0 started; log sequence number 1616839
2016-09-30 11:21:31 123145313034240 [Note] InnoDB: Dumping buffer pool(s) not yet started
OK

To start mysqld at boot time you have to copy
support-files/mysql.server to the right place for your system

PLEASE REMEMBER TO SET A PASSWORD FOR THE MariaDB root USER !
To do so, start the server, then issue the following commands:

'/usr/local/Cellar/mariadb/10.1.17/bin/mysqladmin' -u root password 'new-password'
'/usr/local/Cellar/mariadb/10.1.17/bin/mysqladmin' -u root -h RYANs-iMac-6.local password 'new-password'

Alternatively you can run:
'/usr/local/Cellar/mariadb/10.1.17/bin/mysql_secure_installation'

which will also give you the option of removing the test
databases and anonymous user created by default.  This is
strongly recommended for production servers.

See the MariaDB Knowledgebase at http://mariadb.com/kb or the
MySQL manual for more instructions.

You can start the MariaDB daemon with:
cd '/usr/local/Cellar/mariadb/10.1.17' ; /usr/local/Cellar/mariadb/10.1.17/bin/mysqld_safe --datadir='/usr/local/var/mysql'

You can test the MariaDB daemon with mysql-test-run.pl
cd '/usr/local/Cellar/mariadb/10.1.17/mysql-test' ; perl mysql-test-run.pl

Please report any problems at http://mariadb.org/jira

The latest information about MariaDB is available at http://mariadb.org/.
You can find additional information about the MySQL part at:
http://dev.mysql.com
Support MariaDB development by buying support/new features from MariaDB
Corporation Ab. You can contact us about this at sales@mariadb.com.
Alternatively consider joining our community based development effort:
http://mariadb.com/kb/en/contributing-to-the-mariadb-project/

RYANs-iMac-6:~ alanturing$ mysql.server start
Starting MySQL
. SUCCESS!
RYANs-iMac-6:~ alanturing$

Run 'mysql_secure_installation' Command:

Even in a local development environment, it is still a good idea to perform the secure installation option as follows:

alanturing ~ $ mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user.  If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

Set root password? [Y/n] Y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
 ... Success!


By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
 ... Success!

By default, MariaDB comes with a database named 'test' that anyone can
access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

Create a MariaDB Database, User, and Assign Privileges

The following sequence of commands creates a dataabase, creates a user, assigns the user admin privileges for the database. The '-v' argument increases the verbosity of the command (mysql will return an error message if something fails), and the '-e' flag allows us to pass sql statements in on the command line.

``` alanturing ~ $ mysql -u root -p -ve "create database test" Enter password:

Tags

 PHP  Web Development  Mac  WordPress  Apache  Mariadb  WP-CLI