Search Blog

Author: Joe Meyer
Date: July 17, 2015

When you're working in a business environment, or anytime really, nothing can be more frustrating than having to remember dozens of username and password combinations, all with different password policies. In the past when you wanted to use your Active Directory (or other LDAP) servers to authenticate for your concrete5 system, you were left hacking away at code until you got it to work for you. Every website you had that needed LDAP authentication meant getting a programmer to do some heavy lifting for you and praying that you never needed to change any of your LDAP settings.

With the release of concrete5 version 5.7.4+, we've finally come up with a versitile solution to let you get up and running with LDAP authentication in a matter of minutes, no programming necessary. Simply purchase our ExchangeCore LDAP Authentication package on the concrete5.org marketplace and configure. 

In the past there were very few CMS platforms which supported LDAP. We're hoping with our addition to the concrete5 marketplace others can now have a new alternative to these other platforms, which may not have always been as good of a fit for what you really wanted to accomplish with your website.

Here's a bit of information about our package:

Features

  • Supports Active Directory and OpenLDAP (and any other directory systems which comply to the LDAP version 3 standards)
  • Supports LDAPS and StartTLS
  • Supports group mapping, allowing you to link LDAP security groups to Concrete5 groups that already exist (nested groups are supported)
  • Supports multiple LDAP directories
  • Supports multiple server configuration for high availability / directory server failover
  • Automatically creates concrete5 users on LDAP login
  • Automatically updates users' concrete5 groups based on group mappings when a user logs on
  • A synchronization job is available to synchronize all of your ldap users with concrete5 without having to wait for each one to log in before they show up in the system

Prerequisites

  • concrete5 version 5.7.4 or higher
  • PHP 5.4 is a minimum requirement for this package
  • The PHP LDAP extension must be installed an enabled on your web server
  • Knowledge of your LDAP environment (Servers, Directory Structure, etc)
  • LDAPS / StartTLS configurations may require additional server configuration. Full access on your server may be required to make these configuration changes (I.E. shared hosting accounts may be unable to support these features)

 

For the latest documentation and feature list on this package please check out our package documentation. If you have any questions about capabilities or possible enhancement requests please don't hesitate to send them our way. 

Author: Chris Hougard
Date: September 1, 2014

c57.JPGconcrete5 5.7 is a major change from the previous releases. We have installed a demo you can use to experience the new user interface.

Our demo site can be accessed at  http://c5sandbox.exchangecore.com/. You can login (link is in the site's footer) and use the editing interface using the username demo and the password demouser.

The demo resets every three hours. If someone broke the site just give it a few hours and try again.

Please remember that concrete5 5.7 is still a beta product. You may find bugs (they can be reported on GitHub) and some features may change before the final release.

Author: Joe Meyer
Date: August 11, 2014

Goal: To learn how to properly contribute code changes to concrete5 on github

Difficulty: Moderate

Prerequisites:

  • You must have git scm installed on your machine.
  • For this tutorial we will also be using a free GUI (Graphical User Interface) tool for git called Source Tree.
  • Create an account on github.com (It's important to note that github is simply a repository storage location for git repositories, git and github are not the same thing. Another very popular git repository is bitbucket, but Concrete5 uses github so we'll stick with that for now).

Introduction

There a numerous open source projects available on the web, but many developers are just learning to develop or have never used any sort of version control system for their source code before. In order for open source projects to work though, I believe that you need a community using the product, reporting issues with it, and improving it. The latter two can both be done very effectively using github, and we're going to focus on how anyone with a little bit of development experience can go about contributing code.

Setting Up GitHub

As mentioned above, GitHub is a website used for storing git repositories and a git repository you can think of as a sort of mini file system tracking software / database, but more on that in a bit. To get started with GitHub you'll simply need to visit their website (https://github.com/) and create an account. Now that you have an account, it's usually preferable to create an SSH key (I find using the git gui client or SourceTree is the easiest way) and adding it to your github account via your account settings. If you choose not to do this that's fine, just know that any time you access github (via push, pull, clone, etc) you will be prompted for your user credentials.

Quick Git Overview

Now that we are ready to start accessing git repositories on github, lets do a quick rundown of what git actually does for us. One might think of git as a file system management / tracking database. I use database pretty loosely since really it is just a bunch of tiny files that exist in the file system wherever your repository sits, but essentially they are there to keep track of file differentials, and it uses git binaries in order to make sense of these files.

Here's some vocabulary that you might find important related to Git/Github:

  • Commit - A differential of file changes, this could be file deletes, adds, or changes, and could contain one or many files.
  • Branch - Branches are commonly used to create several different workspaces in a single repository. One very common setup is to use a "Master" branch which sort of acts like a trunk that all other branches (usually feature branches) are based off of. So say for instance you wanted to start working on some validation helpers, you could create a "Helpers" branch, and then say part of the way through with that you were told that you needed to go work on the database abstraction layer, you might create another branch based off of "Master" called "dba-fixes". Now you can keep your helpers merges separate from your database merges, which is important to do so that proper vetting of code can happen. After all, you wouldn't want someone reviewing your helper function changes to affect your database changes or vice versa if they weren't dependent on one another.
  • Merge - Merging is simply the act of taking 2 branches, and merging the changes together. Keep in mind that this could mean that you are merging your local copy of origin/develop to the github copy of origin/develop, because remember that each of these really is their own repository, even if they are clones of one another. More about merging at git-scm.com.
  • Fork - Forking is an important aspect of github rather than git. Forking allows you to create your own copy of a repository on github. This is important because then you are able to create "Pull Requests" from your forked repository.
  • Pull Request - The act of asking the forked repository, to merge changes from a forked repository's branch to one of the forked repository's branches.
Anyway, most of this will hopefully make more sense after we get through an example, so let's get to it.
 

Using Github

From here down I'll be giving more specific instructions on how to use Github for the Concrete5.7 repository. The concepts should apply to any git repositories, but specific examples I find are easier to relate to so here goes...

Creating a Fork

  1. Go to the repository page you wish to Fork. In our case we'll head on over to https://github.com/concrete5/concrete5-5.7.0
  2. Click the "Fork" button in the upper right hand corner of the screen, or you can simply add /fork to the end of the url.
    fork1.PNG
  3.  You will then be redirected to your new forked repository. You can tell that you are on your forked repository by looking in the upper left hand corner of github.
    fork2.PNG

A quick note about forks: Once you have forked the concrete5-5.7.0 repository, you have essentially taken a copy of that git repository at that point in time. Any changes that happen on the concrete5/concrete5-5.7.0 branch will need to be manually merged or rebased into your forked repository in order to receive the latest changes. There is a decent stackoverflow article on how to do this using the git bash tool at http://stackoverflow.com/questions/7244321/how-to-update-github-forked-repository.

Create a Local Clone of the Fork

Now that we've got our very own copy (fork) of the concrete5 repository on github, lets pull that down to our local computer so that we can actually manipulate the code using our favorite IDE or text editor (also worth mentioning, many IDE's have git capabilities built into them that are really kind of nice). To do this we'll fire up Source Tree (link in the pre-requisites at the top of the article).There are those of you who will likely not use Source Tree in favor of the git bash tool, and that's great and you can transfer the concepts here to your own understanding of how that will work. I personally use a mix of the two, because merge conflicts via git bash are simply no fun.

  1. Create a local "Clone" of your newly forked repository by clicking the "Clone / New" button in the upper left corner of Source Tree.
    clone1.PNG
  2. Next you will need to get the Source Path / URL for your git repository. This can be done by copying the URL fromt he clone URL box on github. Once you have this, copy it into the SourceTree window displayed, and hit the "Clone" button. This will start downloading the forked repository to your computer so it may take a little bit if you are using a slower internet connection (about 100mb as of writing this blog).
    clone2.PNG Source Tree Window:
    clone3.PNG 
  3. Once this has completed cloning you should be able to see the repository in the left navigation section of SourceTree. If it is not there however, you can add it by navigating to the cloned repository on your local computer via the File --> Open option inside of SourceTree. Should look something like this:

    clone4.PNG 

Creating a Branch

The next step in the process is to create a "Feature Branch". When working with repositories it is always good to separate the different tasks you are working on into their own branches (commonly called feature branches). This way, you can create pull requests for each feature independently of others.

  1. To do this in SourceTree, right click on the "Branches" navigation and click "New Branch" (You should ensure that you currently have the "Master" branch set as your current branch before doing this. If it is not, you should "checkout" the master branch.)
    branch1.png
  2. Next create a meaningful name for your feature branch. Often times if there is a github issue for the feature / fix you are working on using "fixes-####" is a common naming scheme (so if you are working on a fix for issue #123 you would name your branch "fixes-123"). If you're working on something new that doesn't have an issue make it something short and meaningful to you so you don't confuse it with other branches.
    branch2.PNG
  3. You should now notice that the new branch has been created and checked out as your current working branch. This means that any commits you make will be applied to this new branch, not to "master" or other branches.
    branch3.PNG

 Making Changes to the Repository

At last, we can make our desired changes! You can do this in whatever way you see fit, you can use notepad, vi, sublime, PHPStorm, or any other text editor or IDE you can think of. Modify your files, and watch the changes show up in the "File Status" --> "Working Copy" section in SourceTree.

  1. Once you have some changes made, they should start showing up in the "Working Copy Changes" section. There is also a pane on the right that will show you what the change is. If your changes aren't showing up here, it could be because a .gitignore is excluding the file you are changing from the repository. Here's what it looks like when I modify a file to use the full php tag instead of the short tag.
     changes1.PNG
  2. The next step towards "saving" these changes would be to "Commit" them. A general rule of thumb, do NOT commit anything that you have not tested yet. If it's not working, it shouldn't be a commit, this helps to prevent breakages in the event a commit rollback needs to take place. Another tip is, the more granular your commits the better. For example, if you wanted to make changes to a file, then format the entire file for psr-2 standards, you should do them as 2 separate commits. This makes it easier when the pull request happens to see what you actually changed, vs what you probably used an auto formatter to do. 

    Anyway, to commit your changes in SourceTree typically you stage any file you want to commit first, select the files from your "Working Copy Changes" and click the up arrow to "Stage" the changes.
    changes2.PNG 
  3. Once you have your changes you want to commit in the staging area, simply click the "Commit" button in the upper left corner of SourceTree.
    changes3.PNG
  4. And now you get to create a [hopefully] meaningful commit message. This message helps to give a quick overview of what you've changed in this commit and helps others easily decipher what you've done and if it might affect things they are doing, without them having to parse through all the code you just changed. When you commit, you optionally have the ability to "Push" this commit to your forked repository. Assuming that you have tested and will not need to remove this commit, it is safe to check this box that says "Push commits immediately to [origin]". 
    changes4.PNG
  5. Congratulations, you've now pushed your commit to your forked repository. You can view where each commit is located in SourceTree by clicking back on the "Branches" and selecting the branch you want to look at. 
    changes5.PNG

The Pull Request

Back on github, our web interface has been updated to include our new branch. This is where we'll head to give it the once over quick review and submit our pull request back to the motherland (aka Concrete5/Concrete5-5.7.0).

  1. Head back over to your  github.com forked repository (should look something like https://github.com/EC-Joe/concrete5-5.7.0)
  2. From there select the "Branches" tab
    pull1.PNG
  3. Here you'll find a list of branches that can be found in the fork, as well as those that were brought over from the main repository. What we want to do though, is to create a pull request for our new feature.
    pull2.PNG
  4. Finally, we can start the pull request by clicking the nice shiny button. If you're doing this hopefully you feel pretty confident that your code is production worthy and you have tested it yourself. This page also lists a complete overview of all the changes in case you want to give it a second look.
    pull3.PNG
  5. Once you have created the pull request, it will generate a pull request / issue number in the Concrete5/Concrete5-5.7.0 repository. 
     pull4.PNG
  6. If you make any changes to the branch you create the pull request on, they will automatically be added to the pull request. This means if you want to work on another issue make a new branch, however if perhaps someone comments on your pull requests, and you realize you need to add something you can certainly do so by. 

Removing the Branch

Once your pull request has been merged or close and is no longer needed (it is needed until it gets merged), then you can feel free to delete the branch. This can be done by visiting the pull request and usually at the bottom there is an option to delete the branch. It can also be done from your fork branches page. The final place you will need to remove it from is from your local copy of your fork. This can be done by simply right clicking on the branch in SourceTree and clicking "Delete".

Here's how that looks:
delete1.PNG 

 

I hope that this will help some more of you get started using git / github and contributing to open source projects. For what it's worth, these are great tools to not only be using for open source, but also in house. There are numerous advantages to using these tools in team environments, but even if you're a one person operation, having the ability to review changes made can often be worth the small extra amount of time it takes to maintain a repository.

Author: Joe Meyer
Date: August 2, 2014

Introduction

Having done numerous Concrete5 installs, and having used Concrete5 since version 5.3.x, I thought it would be helpful to share some of the incite we as a company have with regards to the cost of running a Concrete5 website. While many of these concepts will apply globally to any sort of website you create, I thought it would be helpful to those looking at migrating to a Concrete5 site, or possibly even starting a brand new Concrete5 site to know a little about the cost of it.

Open Source - Free

Concrete5 is an open source content management system (cms). Because it is open source, this means that the CMS itself is free to download and use. This also means that if you have suggested improvements, you are able to modify the source code of the CMS and request for your changes to be made for future versions of the software. Right now the source code for Concrete5 resides on GitHub. Version 5.6.x and then there is a separate branch for the new latest and greatest versions 5.7+. You can also download the latest release version of Concrete5 from their website.

For some more interesting reading, click here to see what the CEO of Concrete5, Franz Maruna, has to say about their free product.

Installing Concrete5

Concrete5 like most frameworks, needs to be installed. Fortunately this is relatively simple, we have a how to written on how to Install concrete5 manually. Many web hosts also offer one click installers or quick installers that will do this for you. That said, if you don't know your way around your web hosting platform, and aren't really technical, you may end up needing to hire someone to get you started. This shouldn't be a major cost, since anyone remotely familiar with modern web frameworks should be able to do this install in less than an hour, and anyone who's done it before probably less than 15 minutes.

Updates

As with any sort of framework you use, Concrete5 is constantly undergoing development, and this means that your site is going to need updates to patch bugs, security vulnerabilities, etc. Concrete5 has a built in updater, which often makes updating as simple as hitting a button. However, there have been numerous issues with trying to update reported on the Concrete5 forums, and we've seen quite a few ourselves unfortunately. Usually these are related to skipping multiple versions when trying to update, or if you have some incompatible packages or themes installed. So if you don't consider yourself a particularly tech savy user, you may want to have some technical resources on standby to assist you with upgrading.

We've written a blog on how to Update Concrete5 as well as how to restore an old version of Concrete5 if your upgrade fails. When things go wrong with upgrades, this can turn out to be costly. Not only do you end up with site downtime, but you also end up having to restore an old database, and changing some php configuration files. If you have someone who really knows what they're doing performing these actions, impact should be minimal with near 0 downtime. However, if you are simply a user who edits their site and prefers to stay out of the technical realm, it can often take hours or days trying to get the help you need to get your site back up and running, and if you didn't take a backup before you upgraded, this can often mean a complete website rebuild.

Countless times we have seen failed Concrete5 upgrades/updates, and were called in after the fact to clean up problems. Concrete5 updates work perfectly fine 99% of the time, but please make sure to take backups before performing them or hire someone who has experience performing them. 

Themes and Packages

So you've got your site installed, and while Concrete5 has a lot of awesome features baked into it, sometimes you just need some additional ones. Concrete5 allows programmers to build themes and add ons that allow you to extend Concrete5 with installs. Some of these are free, but some of them can cost money as well. Be sure you know what is available to you out of the box, and what you might have to pay for after the fact. If you've never used Concrete5 I highly recommend their video tutorials to get a quick introduction.

Custom Development

If you need something unique to your website, and you can't find anything to suit your needs in the Concrete5 marketplace, changes are you are going to be looking at some serious out of pocket expense at this point. With development rates varying pretty drastically (typically a decent developer is going to cost between $50-$250 an hour) this can be a large cost to getting your website to function how you want it to.

Web Hosting

Again, we're back to something that all websites need, but you shouldn't forget to include web hosting in the price of your website. There are many shared hosts out there that work great for small web sites (we happen to be one of them). Shared hosting is usually less than $15 per month, and keep in mind that you often get what you pay for. There are many who discourage the use of GoDaddy web hosting for Concrete5, as it has cause numerous problems with installing.

Also, don't forget that you'll also need a domain name so people can access your website. Usually these run somewhere around $10 per year, so not a huge cost by any means.

Cost Summary

In short, Concrete5 can be a very affordable solution to getting a website up and running that is simple to build with and relatively easy to maintain. As Franz mentioned though, developers have gotta eat too, and so if you're looking for some custom functionality or theme, be prepared to pay a little bit for it. 

For those of you looking at using Concrete5 vs Wordpress, try and take cost out of the equation. They both have similar pricing models, you get the base product free and pay for some add-ons / themes. In reality, what you need to be looking at is the functionality you are after, and decide which one will work best for your needs.

 

Need Additional Help or Have Questions?

If you need any additional help or information regarding concrete5, here are some options for you:

  • First and foremost the Concrete5 community. Create an account on www.concrete5.org and post to the forums with any problems or questions you might run across. This helps alert the developers of problems people are having so that the product can be made better or more user friendly in the future.
  • Secondly, please feel free to chat with some members of the concrete5 community over IRC. You can do this by visiting https://kiwiirc.com/client and joining the channel #concrete5. Here you'll find members of the community (and our staff often is frequently hanging around there) who can give you their opinions and experiences.
  • Thirdly, please feel free to contact ExchangeCore's development team. I list us as the last option because while we know what we're doing, we also come at a price and we hate to see you throw money away when there are other options. That said, we have extensive experience with Concrete5 on many levels and you can trust that we know what we're talking about when we give you answers to your questions, we can be reached by visiting our Development Contact Page.

Author: Joe Meyer
Date: July 22, 2014

Goal: To revert your Concrete5 website to a former state via a backup that you had previously taken

Difficulty: Easy

Prerequisites: Obviously in order for this to work you will need to have taken a backup of your Concrete5 website. Another thing that you will need to know is what Concrete5 version you were on when you took the backup (this can also be found by restoring your MySQL backup and looking in the config table).

WARNING: In the event you did NOT take a backup of your database prior to upgrading, do so immediately by whatever means available. At this point you may have lost data or ended up with a corrupt database and you should seek professional help. Please see our additional help section at the bottom of this tutorial.

Introduction

It's not uncommon that when upgrading your Concrete5 site you get done with your upgrade, and errors appear on the page. There are many reasons this can happen, perhaps your memory limit isn't high enough, or maybe you ran out of disk space on your hosting account, or maybe there was some bug in Concrete5 that prevents you from skipping from one version to another without going through some intermediate version manually. As unfortunate as this all is, to date it has always been possible to upgrade to a later version (even if it does require a bit of effort), and to be prepared for cases like this you should ALWAYS take a backup prior to upgrading your site.

With that said lets begin our restore.

Restore the Database

  1. If you used the built in Concrete5 backup tool prior to running your upgrade you will need to go and download a copy of the .sql file that was created. This file can be found using an ftp client or file manager that your web host provides. Here we use CPanel's file manager to go and download a copy of this file from
    /files/backups/dbu_*.sql
    . If you have multiple backups then you can look at the date on the file to help determine which one you want to restore. 
    dbu_sql.PNG

    Note: You should NOT keep these backups sitting out here as it poses a potential security risk in the event that the file permissions are ever changed.

  2. Once you have this file downloaded (note that downloading the file may require you to change the file permissions) then you will need to navigate to your favorite MySQL database tool that is capable of importing the .sql file (In this case and most others you can use PHPMyAdmin which is most likely accessible via your web hosting control panel).

    To do this, we'll open the database in PHPMyAdmin we wish to restore, then we go to the "Import" tab, attach our file and hit "go". Since the Concrete5 sql file that gets created drops the tables if they exist we don't have to worry about cleaning anything up ahead of time. 

    phpmyadmin_import.PNG

Change the Configuration File

When you run a Concrete5 upgrade, it creates a second copy of all the core files, this way it can simply point your installation at this new copy of the files and in the case of failure you just have to point it back at the old files (this is why knowing what version you were on prior to your upgrade is important). In this example, I was trying to upgrade from 5.6.2.1 to 5.6.3.1. Now in order to revert my install back to 5.6.2.1 I have already re-loaded my database (above) and now I need to change my configuration file to point to the appropriate directory.

  1. To determine where you need to point your install to, the first thing you need to do is check in the /updates/ folder in your site root. Does your version you were upgrading from exist there? If so then copy the directory name, you will need that in the next step. If it doesn't exist there then it means that you were previously just pointing your install at the /concrete directory (which will also be explained in the next step.

    In my instance, I have the version I was upgrading from (5.6.2.1) in my /updates/ directory (see screenshot) and it was called concrete5.6.3.1_updater (remember the folder name as it is important for later).
    concrete5_updates.PNG

  2. Now find your way over to /config/site.php. Inside of this file you will find your database connection information, and possibly some other application configurations. What we need to change (or remove) here is the line for the constant 
     <?php define('DIRNAME_APP_UPDATED', 'concrete5.6.3.1_updater');?>
    You'll notice that this is probably trying to point at the directory that you were attempting to upgrade to. Now, if you did not have your previous version in the updates folder, you are safe to simply remove this bit of code from your configuration file, that will point your install to use the /concrete directory as the core again. If you did have your previous version in the updates folder though, change the second argument of the define function to be the directory name (in my case I will change it to be "concrete5.6.2.1_updater" and in the end my config file will look something like this:

    <?php 
    define('DB_SERVER', 'localhost');
    define('DB_USERNAME', 'c5helpuser');
    define('DB_PASSWORD', 'redacted');
    define('DB_DATABASE', 'exchange_c5help');
    define('PASSWORD_SALT', 'redacted');
    define('DIRNAME_APP_UPDATED', 'concrete5.6.2.1_updater');         
    Note: You should take special care to make sure there are no blank lines or whitespace at the end of your site.php file. If there is it can cause images to not display on your website or cause warnings to show up on your site.

And with that you should be reverted back to your previous version and your site should be running again.

Need Additional Help?

In the event that you need additional help with your installation there are several resources available to you.

  • First and foremost the Concrete5 community. Create an account on www.concrete5.org and post to the forums with any problems you might run across. This helps alert the developers of problems people are having so that the product can be made better or more user friendly in the future.
  • Secondly, contact your web host. Often times the updater may error due to php not being configured with enough memory or perhaps it's not running the right version. These are things that your web host should be able to help you address.
  • Thirdly, please feel free to contact ExchangeCore's development team. I list us as the last option because while we know what we're doing, we also come at a price and we hate to see you throw money away when there are other options. That said there are some pretty tough cases and if you do need our help, we can be reached by visiting our Development Contact Page.

Author: Joe Meyer
Date: June 1, 2014

Goal: To explain the differences between the old PHP MySQL extension and it's mysql_query function, and the new PDO (PHP Data Objects) functions.

Difficulty: Easy

Prerequisites: This guide is written under the assumption that you have a pretty good grasp on how programming works in general, such is if's, loops, and function calls. Having used the PHP mysql_* functions would be a big help since the purpose of this guide is to compare that to the PDO functionality.

Why do I care?

For a long time the mysql_* functions have been around with the mysql extension in PHP. They were a fairly simple way of accessing your MySQL databases, and it just worked. But now PHP has deprecated this library as of PHP 5.5, meaning that if you have deprecated warnings turned on in your PHP.ini file you will start to see deprecation messages show up on your site. This also means, that PHP is not going to support these functions in future versions and will likely remove the functionality completely. This means that you need to use an alternative option, mainly PDO_MYSQL (which we'll covere here in a bit) or you can use the newer mysqli extension.

Why is this happening?

Well, to get the full scoop on things you can hop over to PHP's website and view their wiki article on all the reasons why. Really it comes down to the following reasons:

  • Database security with the mysql extensions left a lot to be desired. The old mysql_* functions did not support prepared statements, meaning that things like MySQL injection really had to be handled by something else.
  • The old mysql extension did not support access to all the features in the newer versions of MySQL. Such as the ones listed below:
    • Stored procedures (can't handle multiple result sets)
    • Prepared Statements
    • Encryption (SSL)
    • Compression
    • Full Charset support
  • The old MySQL extension is hard to maintain code. It is not getting new features. Keeping it up to date for working with new versions of libmysql or mysqlnd versions is work, and the PHP team thinks that their time could be better spent improving other things.

Keep in mind that the mysql extension is ancient, it has been around for more than 15 years (since PHP 2.0) and so there are many concerns that such a large code base out in the wild is dependent on the older functions. But like all good things when it comes to technology, everything has an expiration date and ext/mysql's is coming up.

What about applications that use this older functionality?

Well, there is good news and bad news here. Many times when you're bolting on to an application such as wordpress or concrete5, you might be inclined to completely ignore their database abstraction layer altogether. This means that you will have the very unfortunate time of going through all your code and replacing old functions with new ones. However, if you did things properly, usually when programming inside of another application there are function calls exposed which will run your queries through their own database handler, which in turn would call the mysql or mysqli or pdo functions to get the results back. If you used these, then all you have to do is sit back and wait for the core to be updated.

Now, if you're using some ancient system that isn't supported anymore, then it may be time to think about getting it updated or to start looking for alternate solutions. In the meantime, you can continue running on older versions of PHP, and nothing is going to break, but from experience applications that go 15 years without updates become cryptic mysteries to programmers later on after the documentation has been pulled or there has been complete language overhauls done. Best to get ahead of the game on this problem.

I'm a PHP developer. What should I be doing differently?

As mentioned above the replacements for the mysql_* functions are two newer extensions, mysqli (mysql improved) and PDO_MYSQL (PHP Data Objects for MySQL). Below we'll show you some syntax differences and functionality differences between the old MySQL extension, and the new PDO extension. We normally choose to use PDO because of it's OOP style, over MySQLi, but MySQLi is a safe choice and should also be supported for many years to come.

How to Connect to MySQL using PDO

Now down to the meat and potatoes of the guide for programmers. Here I'll show you how you would have originally connected to a MySQL database using the old ext/mysql and an example of how to connect using PDO as well. Note: All code snippits below this connection one also require a connection to be present in order to run so keep this code handy.

<?php

$host = 'localhost';
$database = 'employees';
$username = 'root';
$password = 'P@ssW0rd';

/**
 * START OF CONNECTION CREATION AND DATABASE SELECTION
 */

//connect to database using mysql_* (the old deprecated way)
$link = mysql_connect($host, $username, $password);
if(!$link) {
    die('Error: ' . mysql_error());
}

$dbSelected = mysql_select_db($database, $link);
if(!$dbSelected) {
    die('Error: ' . mysql_error());
}

//connect to database using PDO (the new way)
try {
    $dbh = new PDO('mysql:host='.$host.';dbname='.$database, $username, $password);
} catch (PDOException $e) {
    print 'Error: ' . $e->getMessage() . '<br />';
    die();
}

You can see pretty quickly that PDO uses the object oriented style, and exposes functions on the object instead of setting variables and troubleshooting them individually. It also has it's own exceptions so that they are easy to detect when you build your application instead of a bunch of if blocks looking for different problems on different variable assignments.

How to execute a simple MySQL query using PDO

/**
 * START OF SIMPLE QUERY RUN AND OUTPUT
 */

$query = "SELECT * FROM employees WHERE birth_date < '1955-01-01'";

//Running a simple select statement using mysql_* (the old deprecated way)
$results = mysql_query($query, $link);

while($row = mysql_fetch_assoc($results)) {
    echo $row['first_name'] . ' ' . $row['last_name'] . '<br />';
}


//Running a simple select statement using PDO (the new way)
//method 1:
foreach($dbh->query($query) as $row) {
    echo $row['first_name'] . ' ' . $row['last_name'] . '<br />';
}

//method 2:
$statement = $dbh->query($query);

while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
    echo $row['first_name'] . ' ' . $row['last_name'] . '<br />';
}

//method 3: (This will put all results into an associative array)
$statement = $dbh->query($query);
$resultsArray = $statement->fetchAll(PDO::FETCH_ASSOC);

As you can see with PDO we can accomplish running the query and looping through the results in a much more compact foreach loop if we wish. However, if you're used to the old way with mysql of querying, then looping through in a while loop it might be easiest for you to adopt the second method shown above for PDO. There is also a great fetchAll() function with PDO that will automatically put these items into an associative array for you if you were planning on using them later. This saves you a little bit of coding effort when you have result sets that you need to use multiple times.

How to use variables in a query and protect from SQL Injection

In the olden days of MySQL you really had to go through quite an ordeal to make sure that your user input was sanitized, otherwise you could end up with a Bobby Tables fiasco. Up to this point one might argue that the differences have been pretty negligible between the old way and the new, but I think here you'll see some clear advantages to using prepared statements over the old way of having PHP sanitize your queries for you.

/**
 * START OF QUERY WITH PARAMETERS
 * localhost/pdo.php?number=10001&salary=60117
 */

$employeeID = $_GET['number'];
$salaryID = $_GET['salary'];

//Query with parameters for mysql_* (the old deprecated way)
$results = mysql_query(sprintf("SELECT * FROM salaries WHERE emp_no='%s' AND salary='%s'",
        mysql_real_escape_string($employeeID),mysql_real_escape_string($salaryID))) or die(mysql_error());

$rows = array();
while($row = mysql_fetch_assoc($results)){
    $rows[] = $row;
}

//Query with parameters for PDO (the new way)
//method 1:
$statement = $dbh->prepare('SELECT * FROM salaries WHERE emp_no=? AND salary=?');
$statement->execute(array($employeeID, $salaryID));
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);

//method 2: Bind Value
$statement = $dbh->prepare('SELECT * FROM salaries WHERE emp_no=? AND salary=?');
$statement->bindValue(1, $employeeID, PDO::PARAM_INT);
$statement->bindParam(2, $salaryID, PDO::PARAM_INT);
$statement->execute();
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);

//method 3: Named Placeholders using BindValue
$statement = $dbh->prepare('SELECT * FROM salaries WHERE emp_no=:empId AND salary=:salId');
$statement->bindValue(':empId', $employeeID, PDO::PARAM_INT);
$statement->bindValue(':salId', $salaryID, PDO::PARAM_INT);
$statement->execute();
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);

//method 4: Named Placeholders binding on execute
$statement = $dbh->prepare('SELECT * FROM salaries WHERE emp_no=:empId AND salary=:salId');
$statement->execute(
    array(
        ':empId' => $employeeID,
        ':salId' => $salaryID
    )
);
$rows = $statement->fetchAll(PDO::FETCH_ASSOC);

As you can see there are several much better methods that are far more portable in syntax than the old way of using the mysql functions. Another great advantage is that all PDO functions behave the same way, so now if you migrate over to an oracle database or perhaps microsoft sql, you don't have to worry about changing how you sanitize your queries because all of the code remains the same. You can also see how in some cases the old way could get you cross eyed pretty quick if you were binding 10 or 15 parameters in a query, and the new way (especially with the named placeholders) it is easy to tell what variable belongs where.

Need Additional Help?

If you have an old application and would like someone to take a look at it to see if this breaking change might affect you or you just have questions you think we might be able to help you answer please feel free to contact ExchangeCore's development team by visiting our Development Contact Page.

Author: Joe Meyer
Date: May 22, 2014

Goal: To upload a file to the web server, using HTML and PHP

Difficulty: Easy

Prerequisites: A good grasp on HTML forms, and a working knowledge of PHP is helpful for understanding the guide, but not necessary to get a working product (simply use the last code snippit).

There are many scripts available on the web to assist with uploading files to a server using a web browser. However, it isn't difficult to create a simple user interface that allows someone to upload files using a simple HTML form and PHP script.

In this example, I am going to use an HTML form, and it will submit to the same page. Lets start our file. We'll call it FileUpload.php.

FileUpload.php:

<form action="FileUpload.php" method="post" enctype="multipart/form-data">
    <label for="upload">File:</label>
    <input type="file" name="upload" id="upload"><br/>
    <input type="submit" name="submit" value="Upload">
</form>

 

If we look at the first line in the body, we see our typical html form construct. The action is indicating that we'll submit this form to FileUpload.php (aka this same file), we are using a post request, and the really important part, enctype="multipart/form-data is indicating that this form may contain binary data, which is crucial to allow for the file upload to be able to take place.
Now that we have our form, we can build a little PHP logic into this file to indicate whether or not we just submitted the form (and we'll indicate that the file was uploaded), or if we are waiting on the user to submit (in which case we'll show them the form we just built). It will look something like this:

<?php
    if (isset($_FILES['upload'])) {
        //todo: handle the uploaded file
        echo "Your file was uploaded successfully";
    } else {
    ?>
        <form action="FileUpload.php" method="post" enctype="multipart/form-data">
            <label for="upload">File:</label>
            <input type="file" name="upload" id="upload"><br/>
            <input type="submit" name="submit" value="Upload">
        </form>
    <?php
    }
?>

 

So the reason we're doing this check is because if they submitted a file, I didn't want to show them the form again. If you were submitting your file to a different page obviously you wouldn't need any of this logic, but since I kept it contained in a single file I needed to come up with the different use cases. So in order to do that I simply checked if the upload was submitted to the page or not by using the isset($_POST['upload']) check (to clarify we use "upload" because it is the name attribute on our file input). Now, we need to actually handle the file upload. Right now it's actually being placed in the web servers temporary file storage location which is defined in the php.ini file on the upload_tmp_dir setting. You can retrieve this location by using the function sys_get_temp_dir() for the generic directory, and if you want to know the file location you can use $_FILES['upload']['tmp_name'] (where upload is your form field name).

Moving on to handling the uploaded file. At this point, we're uploading a file, and it's stored in a temporary location on the server. This brings us great joy that we are able to upload files, but it's all for naught if we don't move the file to a more permanent location on disk. So lets get to it:

<?php
    if (isset($_FILES['upload'])) {
        $uploadDir = '/var/www/uploads/'; //path you wish to store you uploaded files
        $uploadedFile = $uploadDir . basename($_FILES['upload']['name']);
        if(move_uploaded_file($_FILES['upload']['tmp_name'], $uploadedFile)) {
            echo 'File was uploaded successfully.';
        } else {
            echo 'There was a problem saving the uploaded file';
        }
        echo '<br/><a href="FileUpload.php">Back to Uploader</a>';
    } else {
    ?>
        <form action="FileUpload.php" method="post" enctype="multipart/form-data">
            <label for="upload">File:</label>
            <input type="file" name="upload" id="upload"><br/>
            <input type="submit" name="submit" value="Upload">
            </form>
        <?php
    }
?>

 

At last, we have a working file uploader, that takes an uploaded file, and moves it over to our specified upload directory. This script should get most novices to file uploading started. There are of course many improvements that could be made on the script, and I'll list some of these considerations below but it'll be up to you to implement the code.

Additional Considerations & Tips for uploading files with PHP

  • PHP maximum upload size: php.ini settings will cause PHP to throw warings/errors when the limits are exceeded. These limit settings that should be adjusted are upload_max_filesize and post_max_size
  • Client side file size validation: This can be important to do, mainly because in the event where a file is to large you will have to first waste the users time as they upload a file which then gets rejected by the server, and to top that off they may end up with an ugly non-user friendly message on the screen.
  • Catching files that exceed the upload limits: can often be done in php by using a hidden input field with a name of MAX_FILE_SIZE which has a value that is equal to the number of bytes that the maximum upload size is. While this can be bypassed by a malicious user, those are the one's who you typically don't care if they see an ugly error message when the server finally gets around to handling it.
  • $_FILES['fieldName']['tmp_name'] is blank: - This can happen if your upload limits aren't set up to handle the size of the file you are trying to upload. Check the PHP.ini file settings again.
  • Other File Attributes: Some other $_FILE['fieldName'] attributes you might find useful include (taken from php.net):
    • $_FILES['fieldName']['name'] - The original name of the file when it was uploaded.
    • $_FILES['fieldName']['type'] - The mime type of the file, if the browser was able to provide this information. An example of this would be image/gif. This mime type is not checked server side so it may be better to implement your own mime type function to retrieve this info based on the file extension.
    • $_FILES['fieldName']['size'] - The size, in bytes, of the uploaded file.
    • $_FILES['fieldName']['tmp_name'] - The temporary path and filename where the uploaded file was stored on the server.
    • $_FILES['fieldName']['error'] - The error code associated with this file upload.
  • Paths must exists ahead of time: - Fair warning that the directory you are trying to move your upload to has to exist before you try and move the file there. This can often be done using the mkdir command.