Linux, Operating Systems, PHP, Ubuntu

Ubuntu 14.10 + Apache 2.4 + PHP-FPM + MariaDB MySQL Server + Composer

Preface

Please understand that this is not a complete how-to guide. There are a lot of things missing from this that you would need to run a site like mine. I just included the bare minimum required to get Apache 2.4, PHP-FPM, MariaDB, and Composer up and running on Ubuntu 14.10. You may find that you need other PHP5 libraries, such as php5-gd, installed as well for this to run your PHP web app of choice. That said, this should be enough to get you started!

Installation

To get started, we will first ensure that we are running the most up to date packages:

sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade

Next, we will install the following dependencies:

sudo apt-get install apache2-mpm-event apache2-utils libapache2-mod-fcgid php5-fpm php5-cli php5-mcrypt php5-mysqlnd curl mariadb-server mariadb-client mariadb-common

Next, install Composer:

curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer

Configuration

We will need to create ourselves a lovely configuration file for PHP-FPM, and save it as php5-fpm.conf:

<FilesMatch .php$>
 SetHandler "proxy:unix:/var/run/php5-fpm.sock|fcgi://localhost/"
</FilesMatch>
<IfModule dir_module>
 DirectoryIndex index.php index.html
</IfModule>

Next we will enable our mods and configurations:

sudo a2enmod fcgid proxy_fcgi
sudo a2enconf php5-fpm
sudo service php5-fpm restart
sudo service apache2 restart
sudo service mysql restart

Testing

Create a file within /var/www/html and call it test.php

<?php
phpinfo();

Navigate to http://localhost/test.php, if you get the output of phpinfo(), then you’ve done everything correctly!

Have fun, and enjoy! Please comments, questions, and corrections below!

CodeIgniter

CodeIgniter HMVC – Protecting your modules

If you have a module in CodeIgniter with HMVC by wiredesignz, you’ll know that all of your modules can be accessed via a browser URL. Here’s a quick snippet that you can use to protect controllers that you don’t want users to directly access, without breaking injection functionality.

/**
 * Prevent direct access to this controller via URL
 *
 * @access public
 * @param  string $method name of method to call
 * @param  array  $params Parameters that would normally get passed on to the method
 * @return void
 */
public function _remap($method, $params = array())
{
    // get controller name
    $controller = mb_strtolower(get_class($this));
     
    if ($controller === mb_strtolower($this->uri->segment(1))) {
        // if requested controller and this controller have the same name
        // show 404 error
        show_404();
    } elseif (method_exists($this, $method))
    {
        // if method exists
        // call method and pass any parameters we recieved onto it.
        return call_user_func_array(array($this, $method), $params);
    } else {
        // method doesn't exist, show error
        show_404();
    }
}

Just paste that method into your controller, and CodeIgniter will check the URI to see if the controller was called directly, otherwise it will operate normally (that’s why I passed $method to it).

  • edit 22/08/2013 to take in account that processing doesn’t necessarily stop after calling the method.
  • edit 18/10/2015 to fix incorrect boolean
CodeIgniter

Setting Library 2.0.0 Release

I’ve gone ahead and rewrote my CI Setting library. This library works like CI_Config, only it uses a database instead of configuration files to store settings. I made it a separate library, rather than extend the CI core library, because CI_Config loads before the database, and I didn’t want to deal with that mess. This was simpler. I’ve done a solution that added database directly to CI_Config, but it was messy, and less configurable by its very nature. Anyway, here’s the code.

Configuration File:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

$config['table'] = 'settings';

/* End of file setting.php */
/* Location: ./application/config/development/setting.php */

 

Library:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Setting Library
 * 
 * Retrieves settings from a database much like the config library retrieves
 * them from files. Complete rewrite from the old one.
 * 
 * NEW
 * ===
 * 
 * Added overrides so that you can specify configs or options on a case
 * by case basis, allowing you to reuse this library with other controllers
 * and models, which may or may not use the same keywords. Also added the
 * ability to specify an alternative database.
 * 
 * @author    Mark LaDoux
 * @copyright Copyright © 2013 Mark LaDoux
 * @version   2.0.0
 */

class Setting
{
	/**
	 * Config File
	 * 
	 * @access protected
	 * @var    string
	 */
	protected $config_file = null;

	/**
	 * Database table
	 * 
	 * @access protected
	 * @var    string
	 */
	protected $table = null;

	/**
	 * Retrieve object from CodeIgniter instance
	 * 
	 * @param  object $param
	 * @return object
	 */
	public function __get($param)
	{
		$object =& get_instance()->$param;
		return $object;
	}

	/**
	 * Load library dependencies and configurations
	 * 
	 * @access public
	 * @return void
	 */
	public function __construct($cfg = array())
	{
		// check overrides
		if(! is_array($cfg))
		{
			// overrides are formatted incorrectly, show an error.
			show_error(
				"Setting library expects an array for manual configuration", 
				503
			);
		}

		// load database
		if(isset($cfg['database']))
		{
			// allow setting library to be configured to use an alternative
			// database. Please refer to CI user guide on as to what to set
			// to this key.
			$this->load->database($cfg['database']);
		}
		else
		{
			// load database based on default configurations.
			$this->load->database();
		}

		// load configuration
		if(isset($cfg['config_file']))
		{
			// allow for the loading of alternative configurations
			$this->config->load($cfg['config_file'], true, true);
			$this->config_file = $cfg['config_file'];
		}
		else
		{
			// load the default configuration file
			$this->config->load('setting', true, true);
			$this->config_file = 'setting';
		}

		// perform overrides
		if (isset($cfg['table'])) {

			// load overriden value
			$this->table = $cfg['table'];
		}
		else
		{
			// load value from a configuration file.
			$this->table = $this->config->item('table', $this->config_file);
		}

		// check to make sure class is configured
		if($this->table === null)
		{
			show_error(
				"Setting library is not configured!",
				503);
		}
	}

	/**
	 * Retrieve a value from a database
	 * 
	 * @access public
	 * @param  string $key
	 * @return mixed
	 */
	public function get($key, $check = false)
	{
		// select the data
		$this->db->where('item', $key);
		$this->db->limit(1);

		// run the query
		$query = $this->db->get($this->table);

		if($check === true)
		{
			// check results
			$output = ($query->num_rows() > 0) ? true : false;
		}
		else
		{
			// get results
			if($query->num_rows() == 0)
			{
				$output = null;
			}
			else
			{
				$result = $query->row();
				$output = $this->_decode($result->value);
			}
		}

		// free up memory
		$query->free_result();

		// return output
		return $output;
	}

	/**
	 * Prepares data for entry into the database, and determines if it needs
	 * to go through an update or an insert process.
	 * 
	 * @access public
	 * @param  array  $data
	 * @return void
	 */
	public function set($data = array())
	{
		if(! is_array($data))
		{
			show_error('Setting class expects item => value pairs to be in an array',
				503
			);
		}

		// cycle through the values
		foreach ($data as $item => $value) 
		{
			// determine if item exists in DB
			$check = $this->get($item, true);

			// encode value for entry
			$clean = $this->_encode($value);

			if($check)
			{
				// update existing item => value pair
				$this->_update($item, $clean);
			}
			else
			{
				// insert new item => value pair
				$this->_insert($item, $clean);
			}
		}
	}

	/**
	 * Encode data so it can go into the database
	 * 
	 * @access protected
	 * @param  mixed     $data
	 * @return string
	 */
	protected function _encode($data)
	{
		// serialize any arrays
		if(is_array($data))
		{
			$out = serialize($data);
		}

		// transform values that won't enter the database normally
		if($data === false) $out = '{{false}}';
		if($data === true)  $out = '{{true}}';
		if($data === null)  $out = '{{null}}';
		if($data == '')     $out = '{{empty}}';

		// return output
		return $out;
	}

	/**
	 * Decode data retrieved from database
	 * 
	 * @access protected
	 * @param  string    $data
	 * @return mixed
	 */
	protected function _decode($data)
	{
		// unserialize any arrays
		$check = @unserialize($data);
		if(is_array($check)) $out = $check;

		// transform encoded values back into their true values
		if($data == '{{false}}') $out = false;
		if($data == '{{true}}')  $out = true;
		if($data == '{{null}}')  $out = null;
		if($data == '{{empty}}') $out = '';

		// return output
		return $out;
	}

	/**
	 * Insert a new item into the database
	 * 
	 * @access protected
	 * @param  string    $item
	 * @param  string    $value
	 * @return void
	 */
	protected function _insert($item, $value)
	{
		// set key => value pairs
		$data['item']  = $item;
		$data['value'] = $value;

		// insert data into the database
		$this->db->insert($this->table, $data);
	}

	/**
	 * Update the value of an existing item in the database
	 * 
	 * @access protected
	 * @param  string    $item
	 * @param  string    $value
	 * @return void
	 */
	protected function _update($item, $value)
	{
		// set key => value pairs
		$data['value'] = $value;

		// locate the existing record
		$this->db->where('item', $item);

		// update the value
		$this->db->update($this->table, $data);
	}
}

/* End of file setting.php */
/* Location: ./application/libraries/setting.php */

I hearby give permission to use or modify this software however you wish, so long as you retain the copyright and credit me. I do not claim that it’s perfect, and I won’t be held responsible for any problems or damages that it may cause you. If you do find an issue with it, please do share with me. I’ll write up the sql for it later and share it soon, but keep in mind, you only need two fields, one for the item name, and one for the item value.

Blog

Internet Go Boom!

Well, it appears that we forgot to pay the internet bill, so I’m writing this entry without the aid of an internet connection to post with. Don’t worry, I’ll upload it tomorrow when we get it turned back on. There are some bright sides to my situation. Since there is no internet access for distracting websites, I can concentrate on working on my projects. I’ve got all my tunes and manuals installed on my system locally in case of this type of emergency.

I decided that I would work on my CMS project. Of course, I’ve got a local copy of Apache installed, and I had just updated all my libraries so that I could be current. I just popped on iTunes, and put my obscenely large collection of Nerdcore music on shuffle. I picked up my keyboard, opened Sublime Text, and started hacking away at all the stuff that I’ve been neglecting. I even managed to come up with an awesome idea for an automated installation system. I was planning on doing one for a while, and wanted to hold of coding till I had an idea that was solid. I wanted to make sure I planned the install framework, so that I could make sure that I made all the adjustable values and default values accordingly. My install system uses a custom CodeIgniter environment setting, that will load configuration files based on the system being in installation mode. I figure I’ll probably develop the baseline of the site, and get to work on the authentication/authorization system.

I also used this time to do some household chores that I’ve been putting off, catch up on Assassins Creed, and watch a few movies that I’ve not seen yet. All in all, this experience isn’t as horrible as it could have been, as I made sure that I had plenty of projects to distract myself with.

Software

PHP Namespace Rant

In planning my PHP projects, I continually run into the same issue. Roughly ninety percent of the best PHP libraries out there do not use namespaces. This is of course a result of namespaces being relatively new, and many projects wanting to maintain compatibility with PHP 5.2 and earlier. What this really translates into for me is, if I want to add a library to one of my projects, which are designed with PHP 5.4+ in mind, I have to manually namespace all the files in that library, else face likely code conflicts.

Now, I may not have any conflicts with the working files, if I include them into the root namespace, until I start trying to autoload. Every large PHP library seems to have its own autoloader, which is fine, until you realize that they are almost always named autoloader. This of course is the first conflict that I almost always run into. I can load multiple autoloaders, but I can’t do so if they all have the same name and the same namespace. The simplest solution is to give them all the same namespace of the vendor. This does work, but it’s a bit messy. The more difficult solution would be to implement the third party autoloader into my own autoloader. This is just too much of a pain in the ass.

You may think that at this point my solution is simple. It’s not, it’s messy, it’s ugly, and it’s downright disgusting. By doing modifying the files I run into issues when it comes to using version control. I am moving all my projects over to git, due to one of it’s awesome features of using submodules. By using submodules, I can include the repository of another library in my project, and have it keep up to date, without having to manually update it myself. The second I modify one of the files, my only option is to fork the project. What this means is, whenever a project is updated, I have to re-pull all the files downstream, and then do my modifications all over again. This is a glorious waste of time and effort. I chose those libraries because I feel that the vendors of those libraries are the best at what they do, and I don’t want to try and reproduce their work all over again. Namespacing non-namespaced code defeats that purpose, forcing me to fork their code, and hope that I don’t miss any major security updates as time goes on.

As you can see, namespacing is awesome, if everyone uses it. So long as everyone tries to be backwards compatible, and produces non-namespaced code, namespacing is a headache. It’s at this point that people will say, why not just get rid of namespaces. I will tell them, that is not the solution. Without namespaces, the conflicts I had will still be there. Namespacing fixes the conflicts. Namespacing gives code context. Context in any language, programmed or spoken, is important. Context does more than tell us what you said, context tells us what you mean. Namespaces are a step forward, the next step is to actually use them.