PHP Developer Guide For Modern Web Development Workflow Setup
I have prepared this guide as a reference for php developers who want to switch from ftp (or sftp) to git (version based) based development.
Fundamental topics including cloud server setup and configuration, local server setup and configuration, git service integration covered in this article.
This guide is more like a general reference of modern web development workflow rather than deep dive into features. Please search certain commands and features mentioned here to better understand and to build better architecture.
If you don’t have a UNIX based computer and UNIX based cloud server, this guide may not suit you well. MACOS as local server and Ubuntu 16 as cloud server used throughout this guide.
1. Local Server Setup and Configuration
We need one basic thing in order to start developing our web app and access it from our browser.
- PHP
- Composer for PHP dependency management (Optional)
- MYSQL or any other database (Optional)
- Memcached or any other in memory key-value store (Optional)
- Watchman or any other file watching service (Optional)
PHP
You can find if php already exists in your system by typing php -v
in your terminal. If it doesn't exist or you want to install the latest version of php (which is 7.x currently) you can install it with homebrew by typing brew install php
in your terminal.
We are not going to install any web server such as apache or nginx since php’s built-in development server works very well.
cd /path/to/my/project
php -S localhost:3000
By executing this command, PHP start accepting web requests through port 3000 and serves your php files pretty much like apache or nginx does.
You can now start writing your php scripts and access them through your browser by typing http://localhost:3000/index.php
in your browser's address bar. Or http://localhost:3000/home
if you have a router written in php.
Today’s development workflows has many enhancing tools to make coding more productive. Following tools mentioned in this section is just to make your workflow more productive.
Composer
Setting up Composer is easy as by typing brew install composer
in your terminal. Once you set it up, you can use any dependency from packagist in your php development server seamlessly.
MYSQL
Go to the MYSQL official website, enter the Downloads section, choose Community Tab and click MYSQL Community Server link. You will find the download link according to your OS. It is easy to install.
You may also need the official GUI tool for MYSQL databases, MYSQL Workbench. Definitely recommend it.
Memcached
Memcached is in memory key-value store. Which means, what you set in memcached store, will exist until memcached server stop or restart. This is very useful for things that your app gets by making complex calculations across requests.
Memcached is a stand alone program and we need to enable its php extension in order to work with our app seamlessly.
How to install Memcached and PHP Extension on MacOS?
# Install memcached
brew install memcached
# Start memcached server
memcached -p 11211
# Install Memcached PHP extension
brew install php70-memcached
# igbinary module required by Memcached
brew install php70-igbinary
Memcached server is online and ready to store our data but we first need to enable Memcached and igbinary PHP extensions.
PHP extensions can be enabled by placing them into the php.ini file.
Let’s learn which php.ini file our development server is using.
Place the code phpinfo();
in some file in your app and view it in your browser. You will see that all configuration parameters and values belong to the running PHP program are in your screen. Search for a line for php.ini
in your browser screen and you will see there is a matching line which is saying "Loaded configuration file". It's value is the php.ini file’s absolute path your development php server use.
Before placing extension directives into the php.ini file we need to look for their absolute paths:
find /usr -name memcached.so
# Outputs something like this: /usr/local/Cellar/php7X-memcached/3.X.X/memcached.so
find /usr -name igbinary.so
# Outputs something like this: /usr/local/Cellar/php7X-igbinary/2.X.X/igbinary.so
Edit php.ini file:
sudo nano /etc/php.ini
# Place extension absolute paths in your php.ini file
extension=/usr/local/Cellar/php7X-igbinary/2.X.X/igbinary.so
extension=/usr/local/Cellar/php7X-memcached/3.X.X/memcached.so
# Save and exit from the file.
Stop your development server by typing CTRL + C
and start again. You can see in your phpinfo();
output whether memcached enabled successfully or not.
Watchman
A file watching service can dramatically increase your productivity by auto refreshing your browser and auto compiling your code. I’m using Facebook’s watchman but any other service satisfy you essentially.
Here is an example i am using in my workflow:
watchman-make -p '*.php' '**/*.styl' --run /path/to/my/project/compile_reload_browser.sh
This code is watching files ending with php
and styl
. It makes compilations and reloads the browser's localhost tab whenever detects a change in those files.
2. Cloud Server Setup and Configuration
Basic tools are covered in this section in order to create a working http server.
Buy A Ubuntu 16 Server Instance from one of the cloud server service providers. Digitalocean in my case.
Configure How You Sign In To The Server
Digitalocean send you an email that contains root password. Open your terminal and type ssh root@YOUR_SERVER_IP
to login your server. Enter your password and configure public-private key authentication if you want.
# Create a new user with sudo permissions
adduser bob
usermod -aG sudo bob
It is recommended to set public-private key authentication for this user too.
Fundamental Services
Following services are fundamental and should be installed in advance:
# Apache web server
sudo apt-get update
sudo apt-get install apache2 -y
sudo service apache2 restart# MYSQL
sudo apt-get install mysql-server -y# PHP
sudo apt-get install python-software-properties -y
sudo add-apt-repository ppa:ondrej/php -y
sudo apt-get update
sudo apt-get install php7.2 -y
sudo apt-get install php7.2-mbstring php7.2-mysql php7.2-xml php7.2-curl php7.2-gd php7.2-pdo-mysql php7.2-xml php7.2-xsl -y# Certbot - Let's Encrypt SSL Client
sudo add-apt-repository ppa:certbot/certbot -y
sudo apt-get update
sudo apt-get install python-certbot-apache -y# Composer
sudo nano composer-setup.sh # contents is in the gist below
sudo bash composer-setup.sh
sudo mv composer.phar /usr/local/bin/composer# Install ZIP and UNZIP. Using it for backup.
sudo apt-get install zip unzip -y
composer-setup.sh
https://gist.github.com/muratgozel/be3cdb7396b5ce429c96561fb097b87c
Configure web server directory permissions:
sudo nano /etc/apache2/envvars
# Add "umask 002" to the end of the file. Save and exit.# Add our current user bob to the www-data group.
sudo usermod -aG www-data bob# Change the group owner of the directory /var/www
sudo chgrp -R www-data /var/www# Every user who belongs to www-data group have read and write access to the everything inside /var/www
sudo chmod 2770 /var/www
Create and activate apache virtual host file in order to make our web app directory readable across web:
sudo nano /etc/apache2/sites-available/example.com.conf # contents is in the gist below.
sudo a2ensite example.com.conf
sudo service apache2 reload
example.com.conf
https://gist.github.com/muratgozel/d5e16ecd8a799a25a4dd49ec49f35c73
What do you think about privacy? There is an easy and powerful solution for that:
# Configure SSL
sudo certbot --apache -d example.com -d www.example.com
Our web app is now accessible over web.
We now have a local server and a cloud server. We want to be able to push the code changes in the local server to the cloud server fast and seamlessly.
3. GIT Integration
A Gitlab will be used in this article but many GIT service providers have similar ways in updating cloud codebase.
Repository
A repository is a central location of the app’s codebase. Any change made by anyone, should be pushed to the repository. Cloud server should automatically fetch these changes in order to create a productive workflow.
Create a private repository. Such as web-app
.
Get the SSH endpoint. Will be something like git@gitlab.com:web-app.git
Configure Local Server
What we did below is to configure the local computer to push code changes to the repository we have just created above.
# Configure GIT
git config --global user.name "Firstname Lastname"
git config --global user.email username@email.com# Activate GIT in your app
cd /path/to/my/project
git init# Configure to push your changes to the repository you've just created.
git remote add origin git@gitlab.com:web-app.git# Create a production branch.
# We want cloud server to be updated only if production branch changes.
git checkout -b production# Send everything in your project to the repository
git add * && git commit -m "Added XYZ feature"
git push -u origin production
Code in our local server have been sent to the repo. Now on, every time we did changes in the codebase, we are going to execute:
git add *
git commit -m "Added ABC feature"
git push
If Gitlab asking you a password in every push, you may better configure SSH authentication between your computer and Gitlab. Gitlab has a good documentation for that.
Configure Cloud Server
What we did below is to configure the cloud server to fetch code changes in the repository.
Make sure you have set up SSH authentication between your server and Gitlab. Again, Gitlab has a good documentation for that.
Download all files in the app to the cloud server:
cd /var/www
git clone git@gitlab.com:web-app.git
Now on, every time code changed in the repository’s production branch, the following commands has to be executed:
cd /var/www/web-app
git reset --hard origin/production
git pull
What commands above do is deploying. It fetches updates from the repository and updates the live folder accessible over the web.
This is seamless deployment for our app but it is not fast. Every time we push in our local computer to the repository, we need to connect our server and execute the commands above.
Auto-Deploy
Web app, fetches the changes automatically in the production branch of the repository whenever we push to the production branch from our local computer.
- Configure A Webhook
- Configure SSH Authentication Between Apache User and Gitlab
- Create A Deploy Script
A Webhook
A webhook is the way of telling “send request to this URL when this happens in a repository” in the Git Service.
In Gitlab, go to project Settings — Integrations page.
Create a new webhook by entering your preferred url (such as https://example.com/deploy.php), a random string (such as “as9s2pd7sdhs5”) and choosing only the “Push Events” trigger.
We have created a webhook. But we need a real working deploy script located in that path.
SSH Authentication Between Apache User and Gitlab
Apache user need to be able to fetch the code changes from the repository because deploy script is run by apache.
Login to the cloud server and generate public-private key pair for the www-data user.
# Create public-private key pair for www-data user.
sudo -u www-data ssh-keygen -t rsa -b 2048 -P ""
# Print public key to the screen and copy it
sudo cat /var/www/.ssh/id_rsa.pub
Now on Gitlab, go to project Settings — Repository page. Expand Deploy Keys and paste the public key you have just copied into the Key field.
Turn back to cloud server and approve authenticity of Gitlab:
cd /var/www/web-app
sudo -u www-data git pull
Apache user is now able to get data from the repository.
Deploy Script
The deploy script below, is not ready for production. Take the necessary precautions and make it live on the path you specified while creating the webhook.
deploy.php
define('GITLAB_TOKEN', 'as9s2pd7sdhs5');
define('GITLAB_BRANCH', 'production');if ($_SERVER['HTTP_X_GITLAB_TOKEN'] != GITLAB_TOKEN) {
header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request.', true, 400);
die();
}$output = shell_exec('cd /var/www/web-app && git reset --hard origin/'.GITLAB_BRANCH.' && git pull 2>&1');header($_SERVER['SERVER_PROTOCOL'].' 200 Ok', true, 200);
On cloud server:
cd /var/www/web-app
nano deploy.php
# Paste the contents of your deploy script, save and exit.
Gitlab will send a request to the deploy script whenever someone pushes the code to the production branch and app will automatically update its live codebase.
🎩