Search Blog

Author: Joe Meyer
Date: December 18, 2015

It's not infrequent we come across sites running on PHP 5.3 and cringe at the thought of running a potentially insecure and unsupported version of the language. But aside from that we should take care to look at the other tangible benefits of running a more modern version of PHP, namely performance and stability. With the latest version of concrete5 we're able to even run PHP7 and we're about to show you what's so exciting about that.

Below is a table of our findings. These tests were run against an account on our very own standard shared hosting plan, so nobody should think that "I can only get these kinds of results by having my own VPS or Dedicated Server and optimizing it to the max". The requests were made against the default home page of concrete5 version 7.

About the results

The sections with Concurrency Level, Average TTFB (Time to first byte), and Results File are the results of ApacheBenchmark. The TTFB is measured in milliseconds, and as you can see the more requests per second we throw at it the more it slows down. But perhaps what's most interesting here is that PHP7 with opcache at 10 concurrent requests averages a lower TTFB than PHP 5.3 does serving up only 1 requests at a time, and also outperforms all other versions of PHP at 10 concurrent requests by at least 53% and as much as 214%.


The Siege Results, were taken from a HTTP stress tester known as Siege. These are the results that came from throwing ~40 concurrent requests repeatedly at the server for 60 seconds, when one of the requests finished another one was sent. The Transactions is the number of successful requests processed. The Availability percentage indicates out of the 40 "users" making request, how often did the request succeed. Finally the Response Time in seconds is on average how long did it take to receive a response.

As we can see from these results, PHP7 with opcache enabled is the clear winner when it comes to performance, serving up requests 4x faster than PHP 5.3. Additionally the average response time under load was 1/4 of what it was and when not under load it was consistently half.  Another aspect which I feel is important when looking at the results is the Availability. Most websites don't just have a nice steady stream of requests all day long, there are lulls and spikes in traffic. These results show a pretty clear distinction on scalability with the newer versions of PHP, and the overall performance improvements that having opcache can have for you. By default PHP 5.4 and PHP 5.3 don't even offer opcache, you have to manually add the PECL module and many web hosts don't do this.




  PHP7 OpCache PHP7 PHP5.6 OpCache PHP5.6 PHP5.5 OpCache PHP5.5 PHP5.4 PHP5.3
Concurrency Level 1 1 1 1 1 1 1 1
Average TTFB 419 711 561 895 645 633 815 835
Results File PHP7_opcache_1.txt PHP7_1.txt PHP5.6_opcache_1.txt PHP5.6_1.txt PHP5.5_opcache_1.txt PHP5.5_1.txt PHP5.4_1.txt PHP5.3_1.txt
Concurrency Level 2 2 2 2 2 2 2 2
Average TTFB 438 723 554 900 662 635 819 839
Results File PHP7_opcache_2.txt PHP7_2.txt PHP5.6_opcache_2.txt PHP5.6_2.txt PHP5.5_opcache_2.txt PHP5.5_2.txt PHP5.4_2.txt PHP5.3_2.txt
Concurrency Level 5 5 5 5 5 5 5 5
Average TTFB 545 731 724 902 609 633 852 872
Results File PHP7_opcache_5.txt PHP7_5.txt PHP5.6_opcache_5.txt PHP5.6_5.txt PHP5.5_opcache_5.txt PHP5.5_5.txt PHP5.4_5.txt PHP5.3_5.txt
Concurrency Level 10 10 10 10 10 10 10 10
Average TTFB 753 1265 1166 1616 1023 1159 1525 1545
Results File PHP7_opcache_10.txt PHP7_10.txt PHP5.6_opcache_10.txt PHP5.6_10.txt PHP5.5_opcache_10.txt PHP5.5_10.txt PHP5.4_10.txt PHP5.3_10.txt
SIEGE RESULTS PHP7_opcache_siege.txt PHP7_siege.txt PHP5.6_opcache_siege.txt PHP5.6_siege.txt PHP5.5_opcache_siege.txt PHP5.5_siege.txt PHP5.4_siege.txt PHP5.3_siege.txt
Transactions 1037 511 682 362 499 339 355 276
Availability 87.51 62.17 75.36 51.06 71.59% 50.67% 54.67% 43.40%
Response Time (seconds) 2.24 4.5 3.37 6.22 4.62 6.65 6.97 8.06


PHP7 Concrete5

Concrete5 Page Speeds

Whether you're looking for the version of PHP to serve up the most requests or have the fastest load times, PHP7 will definitely serve you well. If you'd rather go with something a little bit less bleeding edge and more widely tested across concrete5 websites, PHP 5.6 with opcache is most likely your next best bet. If you can't get opcache, try checking if you are offered xcache, as it also has some decent performance advantages (even though I didn't benchmark in the above list I can tell you from experience that they produce similar results to opcache). If you're curious about HOW fast it really is, go ahead and check out and witness PHP7 for yourself on exchangecore shared hosting. You can even try out the editing interface by using the username demo and the password demouser. Very frequently you can have the entire page RENDERED in under a second.

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 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:


  • 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


  • 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 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


  • 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 (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).


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 ( 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
  • 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
  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.
  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.

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

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.
  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:
  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:


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.)
  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.
  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.

 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.
  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.
  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.
  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]". 
  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. 

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 forked repository (should look something like
  2. From there select the "Branches" tab
  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.
  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.
  5. Once you have created the pull request, it will generate a pull request / issue number in the Concrete5/Concrete5-5.7.0 repository. 
  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:


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


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.


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 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 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.


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
    . If you have multiple backups then you can look at the date on the file to help determine which one you want to restore. 

    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. 


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 to Now in order to revert my install back to 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 ( 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).

  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:

    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 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.


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


//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 />';

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


$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.

 * 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);
$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);
$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');
        ':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.