Archive for the ‘Development’ Category

Can’t use function return value in write context

September 26, 2014 Leave a comment

Did you ever develop some nice code, then simply wanted to check if a string was only composed of white spaces or tabs, and used something like this:

if (!empty(trim($string))) { ... }

…only to get a bad error appear (only on your PHP 5.4 server) in the middle of your app, like this?:

Fatal error: Can't use function return value in write context

Well, this has a simple explanation…

Prior to PHP 5.5, the empty() function did not accept something else than a variable. From


Prior to PHP 5.5, empty() only supports variables; anything else will result in a parse error. In other words, the following will not work: empty(trim($name)). Instead, use trim($name) == false.

So the easy solution (but kind of making PHP feel beyond help) is to do what is recommended in the comment (trim($string) == false) or decompose your statement (although it will be less efficient than the above):

$string = trim($string);
if (!empty($string)) { ... }

This is due to the fact that empty() is a language construct, not really a function (it works as a function to make things easier). So, under the same logic, this will not work with other language constructs, like echo, require, etc (although the explanation in empty’s documentation is clearer than any other).

Categories: Development, English, php Tags:

Crowdin terms review for Chamilo LMS translations

As we migrate to gettext for Chamilo LMS v10, we are also looking for a platform to host our translation system (considering our current translation system does not support gettext).

We will migrate most existing translations, but we were looking for the right platform to manage the translations as a community. One tool that attracted our attention was Pootle, a Python-based open source translation system that seems to be lead by the right, passionate people.

In this first phase though, we really need to avoid being distracted by other things than the development of the core code of Chamilo LMS v10. This is why we looked for a hosted solution, with an existing community, preferrably with special plans for open source projects (like Github has).

So we found, with apparently all the features we will need at first. We also looked at terms and conditions and privacy terms. Terms and conditions indicate that all content translated remain the property of the user, which is good, but we will have to manage some kind of agreement with all translators that their translation work will be considered Creative Commons BY-SA. Finally, privacy terms are pretty reasonnable and respectful of our privacy, but they *do* indicate that can send promotional e-mails about its services to all members, which means that translators will get a (hopefully reasonnable) amount of spam (I call it spam anyway), but that’s limited to services only. This being said, we will try it as a reduced core of developers for a while and see if the mails flow is reasonnable before we generalize its use.

So if you want to try it, you should be able to take a look within a few days from now on

Using php5-memcached to store sessions on distributed servers

This article is an extension of the previous article about storing sessions in Memcached with PHP.

Using Memcached for sessions storage is generally a matter of speed (storing them in memory is faster than on disk) and of scalability (using a Memcached server allows you to have several web servers serving the same PHP application in a seamless way).

In the previous article, we mentioned (3 years ago) that the php5-memcached extension did not seem to manage the storage of sessions quite well, or at least that the configuration of such setup was not well documented. This time, we’ve been able to do it with php5-memcached. This time also, we’re talking about distributing Memcached on several servers. Typically, in a cloud environment with several web servers and no dedicated memcached server, or a need for some kind of fail-over solution for the memcached server. We’re setting this up on 3 Ubuntu 14.04 servers in 64bit that act as load-balanced web servers behind a load balancer managed by Nginx. If you use another distribution or operating system, you might need to adapt the commands we’re showing here.

Installing Memcached + PHP

So the few commands you need to launch first are to install the required software, on the 3 web servers (you can either do that on one of them then replicate the image if you are using Cloud instances, or you can install simultaneously on your 3 web servers using ClusterSSH, for example, with the cssh server1 server2 server3 command) :

sudo apt-get install memcached php5-memcached
sudo service apache2 restart

The second command is to make sure Apache understands the php5-memcached extension is there.

In order to enable the connection to Memcached from the different load-balanced servers, we need to change the Memcached configuration to listen on the external IP address. Check the IP address with /sbin/ifconfig. The Memcached configuration file is located in /etc/memcached.conf. Locate the “-l” option and change “” for your external IP, then save and close the file. Note that this might introduce a security flaw, where you are possibly opening the connection to your Memcached server to the outside world. You can prevent that using a firewall (iptables is a big classic, available on Ubuntu)

Next, restart the Memcached daemon:

sudo service memcached restart

Now you have a Memcached server running on each of your web servers, accessible from the other web servers. To test this, connect to any of your web servers and try a telnet connection on the default Memcached port: 11211, like so:

user@server1$ telnet ip-server2 11211

To get out of there, just type “quit” and Enter.

OK, so now we have 3 Memcached servers, we only need to wrap up configuring PHP to use these Memcached servers to store sessions.

Configuring PHP to use Memcached as session storage

This is done by editing your Apache VirtualHost files (on each web server) and adding (before the closing </VirtualHost> tag) the following PHP settings:

 php_admin_value session.save_handler memcached
 php_admin_value session.save_path "ip-server1:11211,ip-server2:11211,ip-server3:11211"

Now reload your web server:

 sudo service apache2 reload

You should now be able to connect to your web application using the distributed Memcached server as a session storage (you usually don’t need to change anything in your application itself, but some might exceptionally define their own session storage policy).

The dangers of using a distributed Memcached server

Apart from the possible open access to your Memcached server previously mentioned, which is particularly security-related, you have to take another danger, mostly high-availability related, into account.

When using a distributed Memcached server configuration, it is important to understand that it works as sharded spaces configuration. That is, it doesn’t store the same sessions over on the various available Memcached server. It only stores each single session in one single server. The decision of where it will store the session is out of the context of this article, but it means that, if you have 300 users with active sessions on your system at any one time, and one of your web servers goes down, you still have 2 web servers and 2 Memcached servers, but ultimately around 100 users will loose their session (that was stored on the web server that went down).

Worst: the PHP configuration will not understand this, and still try to send sessions to the server that was considered to hold these 100 sessions, making it impossible for the users to login again until the corresponding Memcached server is back up (unless you change the configuration in your PHP configuration).

This is why you have to consider 2 things, and why this article is just one step in the right direction:

  • you should configure the Memcached servers from inside your application for the sessions management (as such, you should have a save_handler defined inside it and check for the availability of each server *before* you store the session in it)
  • if your sessions are critical, you should always have some kind of data persistence mechanism, whereby (for example), you store the session in the database once every ten times it is modified

We hope this was of some use to you in understanding how to use Memcached for sessions storage in PHP. Please don’t hesitate to leave questions or comments below.

Using Chamilo juju charm to setup a dev environment on Digital Ocean

June 23, 2014 8 comments

If you’re in a hurry/on speed, know this:

  • this procedure is slightly more difficult (so longer) than installing the charm on Amazon
  • you can skip directly to “Installing Juju”
  • if you already have juju installed, you can skip to the last 2 lines of the “Installing juju” section
  • if you already have juju-docean installed and configured, you can skip directly to “Provisioning VMs”
  • otherwise, just continue reading, it’s worth a few minutes…

This tutorial regroups a lot of advanced notions, so if you want to know more about one of the following elements, please follow these links:

Before anything else, please note that the following is highly experimental. There are still a series of issues that should be worked out in order to make this process failproof.

Basic setup

Before we start using commands and stuff, you’ll have to note the following:

  • We are using a Chamilo Charm developed by José Antonio Rey (kudos to him) as a voluntary contribution to the project
  • Charms are configurations to install applications (and stuff) inside the Juju framework
  • The Juju framework is developed by the Ubuntu team, so we’re using an Ubuntu (14.04) desktop (or in this case laptop) to launch all the following
  • Digital Ocean is one cloud hosting provider, which is particularly cheap and good for development purposes. The “default” environment for Juju is Amazon, so we’ll have a few additional steps because of this choice. The Digital Ocean plugin to Juju is developed by geekmush on Github, and as far as I know he is not related to either Ubuntu nor Digital Ocean, so he is also worth praising for his contribution
  • Chamilo requires a web server and a database server. In this Charm, it is assumed that we want both of these on separate virtual machines, so you will need two of them (unless you change the parameters a little)
  • Juju is written in Go but relies on several Python libraries. As such, you’ll have to have python installed on your system and maybe Juju will shout because it is missing a few dependencies. Notably, I installed python3-yaml to avoid a few warnings (it is required for the following, although the installer for Juju says it’s optional)

Installing Juju

On a default Ubuntu desktop installation, you’ll have to install Juju first. Because we are going to use Juju connected to Digital Ocean, we need a recent version of Juju, so let’s add it the unconventional way (with the ppa), launching the following on the command line:

sudo add-apt-repository ppa:juju/devel
sudo apt-get update && apt-get install juju
juju version

For some reason, in my case, this created my home directory’s .juju/ folder with root permissions, which then prevented me to reconfigure my environment (requirement for the Digital Ocean Juju plugin), so I changed permissions (my user is “ywarnier”, so change that to your user):

sudo chown -R ywarnier:ywarnier .juju

Then we need to install the juju-docean plugin:

sudo apt-get install python3-yaml
sudo pip install -U juju-docean

Setting up Digital Ocean access

Now we need to configure our Digital Ocean (D.O.) API so the system will be able to call D.O. in our place and create instances (and stuff).

You first need to grab your API key, client ID and SSH key ID from the Digital Ocean interface. You can do that from the Digital Ocean API page. Obviously, you need a Digital Ocean account to do this and a few bucks of credit (although you can get $10 free credit from several places). If your API key says “Hidden”, that’s because you must have it stored somewhere already (for other services?). If you don’t, you’ll have to re-generate one. Your SSH key ID is the name you gave to the SSH key you use from your computer to connect to your new instances. If you don’t have it, that’s probably because you haven’t configured any. Please do in the “SSH Keys” menu item on the left side of your D.O. panel.

 export DO_CLIENT_ID=aseriesof21alphanumericalcharacters
 export DO_SSH_KEY="user@computer"
 export DO_API_KEY=aseriesof32characters

Setting up the Digital Ocean Juju environment

Now we need a bit of manual config to be able to use Digital Ocean (last bit, promised). Edit the ~/.juju/environments.yaml file and paste the following:

 type: manual
 bootstrap-host: null
 bootstrap-user: root

Just a note: the “type: manual” line implies it is a bit more complicated than on amazon later on, and we will have to launch a few more commands to provision new machines *before* we deploy Chamilo.

Generating the Juju environment

Now we’re going to create our Juju controller. The Juju controller can be an independent Virtual Machine (VM), or it can be the same as the one on which you will deploy Chamilo. It all depends on your budget and your requirements.

juju docean bootstrap --constraints="mem=1g, region=nyc1"
  2014/06/22 11:50.24:INFO Launching bootstrap host
  2014/06/22 11:51.29:INFO Bootstrapping environmen

Note that we took a decision to use a 1GB (RAM) VM here (mem=1g), in a datacenter in New York (region=nyc1). For the record, I tried creating them in nyc2, which is also a valid D.O. datacenter, but it failed miserably (sometimes not creating the VM, sometimes creating it without IP, sometimes creating it fully, but in the end never returning with a proper success response for my environment to be created), so sticking to nyc1 is probably a reasonable time-saver.

Provisioning VMs

To be able to deploy Chamilo, we’ll use two VMs: one for the web server and one for the database

juju docean add-machine -n 2 --constraints="mem=1g, region=nyc1"
2014/06/22 12:44.59:INFO Launching 2 instances
2014/06/22 12:46.42:INFO Registered id:1908893 name:digitalocean-8d14c9bc671555ff872d8d6731f84d68 ip: as juju machine
2014/06/22 12:49.08:INFO Registered id:1908894 name:digitalocean-a9ba29cfe55549f58e5f7e365199c5ed ip: as juju machine

Now, the “-n 2” above allows you to create these 2 instances, but you could also launch 2 different instances of different properties, doing it one by one. In our case, I suggest you use version Trusty of Ubuntu for the MySQL machine, to avoid a little bug in the Precise version of the charm:

juju docean add-machine --constraints="mem=2g, region=nyc1"
juju docean add-machine --series=trusty --constraints="mem=1g, region=nyc1"

The important thing here being that you can later identify the machine itself by a simple ID, using juju status:

juju status
environment: digitalocean
  agent-state: started
  agent-version: 1.19.3
  instance-id: 'manual:'
  series: precise
  hardware: arch=amd64 cpu-cores=1 mem=994M
  state-server-member-status: has-vote
  agent-state: started
  agent-version: 1.19.3
  instance-id: manual:
  series: precise
  hardware: arch=amd64 cpu-cores=1 mem=994M
  agent-state: started
  agent-version: 1.19.3
  instance-id: manual:
  series: trusty
  hardware: arch=amd64 cpu-cores=1 mem=994M

If you made a mistake at some point or just wanna try things out, you can destroy these instances with

juju docean terminate-machine 1

where “1” is the ID of the machine, as shown above before each of them.

Deploying Chamilo

Now we’ve got our machines, we just need to deploy the Chamilo Charm and the MySQL Charm (you need MySQL to run Chamilo):

juju deploy cs:~jose/chamilo --to 1
juju deploy mysql --to 2

Please note that the “–to n” option is to specify on which machine you want to deploy the selected service.

Now, we need to configure Chamilo a little. We’re going to give it a domain name (you’ll have to redirect this domain name to the IP of the first machine – the one with the Chamilo service – in order to use it when ready) and a password for the “admin” user (the user created by default):

juju set chamilo pass=blabla

Now we still need to tell Juju to link the Chamilo service with the MySQL service:

juju add-relation chamilo mysql

And finally, apply all the above and expose the chamilo service to the public:

juju expose chamilo

If something goes wrong with a service, you can always remove it with:

juju destroy-service chamilo

You can replace “chamilo” by the service with which you are having the issue, of course. If that doesn’t work out, you can always remove (terminate) the machine itself (see above).

Useful tricks

You can connect at any time to any of your virtual machines through the command

juju ssh chamilo/0

where “chamilo/0” is the name appearing below “units” in your services.

You can check the status of all your instances with

juju status

Note that, sometimes, you might end up with dozens or hundreds of instances. In this case, it won’t be as practical to show the status of all instances (I have no solution for that now, but I’m sure there is a way to filter the results of a juju status).

You can launch a command on the virtual machines’ command line like this:

juju run --service chamilo "tail /var/log/juju/unit-chamilo-0.log"

This way, you are actually executing the command remotely and getting the results locally.

You can also see the error log locally, connecting in SSH (first) and then launching:

 tail /var/log/juju/unit-chamilo-0.log

Obviously, that gives you a little more flexibility.

Notes about unexpected errors

One of the “silent” things is that Juju considers the default machine to be Ubuntu Precise. In the case of MySQL, the default Charm is configured for Trusty. This means that if you want to install this package, you need to install a virtual machine in Trusty. Otherwise, you might get some other issues. In my case, the Precise Charm didn’t really work (missing yaml), so I decided to go for Trusty.

You can choose the distribution of your machine with –series=trusty, for example:

juju docean add-machine --series=trusty --constraints="mem=2g, region=nyc1"

We tested the chamilo charm relatively extensively.

Unmounting the whole thing

If this was just a test, and you’re happy, maybe you want to remove everything. If so, the quickest way to do that is to launch a destroy-environment command, but you will first need to destroy each machine and, before that, each services that :

juju destroy service chamilo mysql
juju destroy machine 1 2
juju destroy-environment digitalocean

This should reasonnably quickly remove the whole setup.

You should still check your Digital Ocean’s dashboard, though, as apparently it doesn’t always delete the nodes you created with Juju…

Quick commands list for the impatient

Assuming you’re running Ubuntu 14.04 and that you know which values to change in the commands below:

sudo add-apt-repository ppa:juju/devel
sudo apt-get update && apt-get install juju
sudo chmod -R 0700 .juju
sudo apt-get install python3-yaml
sudo pip install -U juju-docean
export DO_CLIENT_ID=aseriesof21alphanumericalcharacters 
export DO_SSH_KEY="user@computer" 
export DO_API_KEY=aseriesof32characters
juju docean bootstrap --constraints="mem=1g, region=nyc1"
juju docean add-machine --constraints="mem=2g, region=nyc1"
juju docean add-machine --series=trusty --constraints="mem=1g, region=nyc1"
juju deploy cs:~jose/chamilo --to 1
juju deploy mysql --to 2
juju set chamilo pass=blabla
juju add-relation chamilo mysql
juju expose chamilo

And connect your browser to (that you must have redirected to the corresponding IP first) and login with admin/blabla.


How to not write a condition for code readability

Sometimes you find a perl in some code and it might take you a while to understand.

In the sessions management code of Chamilo, I found this the other day, then decided to leave it for later because I was in a hurry. Then today, I found it again, and again… it took me about 10 minutes to make sure of how the code was wrong and how to make it right.

 if ( $session->has('starttime') && $session->is_valid()) {
 } else {
   $session->write('starttime', time());

To give you the context, we want to know whether we should destroy the session because its time is expired, or whether to extend the period of time for which it is valid (because it is still active now, and we just refreshed a page, which lead us to this piece of code).

OK, so as you can see, we check if the session has a value called “starttime”. If it hasn’t we pass to the “else” statement and define one, starting from now.

If, however, the session has a start time, we check whether it… “is valid” (that’s what the function’s called by, right?) and, if it is… we destroy the session.

Uh… what?

Yeah… if it is valid, we destroy it (DIE SESSION, DIE!)

OK, so something’s wrong here. Let’s check that “is_valid()” method (there was documentation, but it was wrong as well, so let’s avoid more confusion)

  public function is_valid()
    return !$this->is_stalled();
  function is_stalled()
    return $this->end_time() >= time();

OK, so as you can see, is_valid() is the boolean opposite of is_stalled(). “Is stalled”, in my understanding of English (and I checked a dictionnary, just to make sure) means “blocked” (in some way or another). Now it doesn’t really apply to something that is either “valid” or “expired”, either. And this is part of the problem.

The is_stalled() method, here, checks if the end_time for the session is greater than the current time. Obviously, we’ll agree (between you and me) that if the expiration time is greater (further in the future) than the current time, this means that we still have time before it expires, right?

Which means that, if the condition in is_stalled() is matched, we are actually *not* stalled at all. We are good to go.

So I believe the issue here was the developer either confusing the word “stalled” for something else or, as it doesn’t really apply to “expired” or “not expired”, that he simply badly picked the word describing the state.

In any case, now that we’ve reviewed these few lines of code, I’m sure you’ll agree it would be much clearer this way:

 /* caller code */
 if ( $session->has('starttime') && $session->is_expired()) {
 } else {
   $session->write('starttime', time());
 /* called method */
 public function is_expired()
   return $this->end_time() < time();

Conclusion: always choose your functions naming carefully. Others *need* to be able to understand your code in order to extend it.

gem install mysql2 on Debian Wheezy with MariaDB 10

April 13, 2014 2 comments

Just in case you would be in this situation (and given the fact I couldn’t find online information about it), if you are ever installing the mysql2 gem on a Debian Wheezy system with MariaDB 10 (I would guess a rather rare situation), you might get this error:

# gem install mysql2 -v=0.3.11
Building native extensions. This could take a while...
ERROR: Error installing mysql2:
 ERROR: Failed to build gem native extension.

 /usr/bin/ruby1.9.1 extconf.rb
checking for rb_thread_blocking_region()... yes
checking for rb_wait_for_single_fd()... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lmygcc... no
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers. Check the mkmf.log file for more
details. You may need configuration options.

Provided configuration options:

Gem files will remain installed in /var/lib/gems/1.9.1/gems/mysql2-0.3.11 for inspection.
Results logged to /var/lib/gems/1.9.1/gems/mysql2-0.3.11/ext/mysql2/gem_make.out


If you find yourself in that situation, the solution is apparently to first issue an

apt-get install libmariadbd-dev

And then repeat the gem install mysql2 command. Done.

Creating multiple git forks using upstream branches

December 26, 2013 Leave a comment

Working with Git is… complex. It’s not that it’s from another world, and the complexity is probably worth it considering the crazy things it allows you to do, but sometimes it’s just mind-bloggingly complex to understand how to do things right.

Recently, we’ve had to manage a series of projects with changes that cannot be applied directly to our original Git repository on Github, so we decided, after giving it some thought, to make several forks of the project (instead of branches), to manage more clearly permissions and the real intentions behind each of the forks.

This would allow us a few important things:

  • define precisely permissions based on the repository
  • use the repository to pull changes directly on our servers
  • have some level of local customization on each server, using local commits (and subsequent merges)
  • keep easy track of the changes that have been made on requests from some of our “customers” (they’re not necessarily commercial customers – sometimes they’re just users who decided to go crazy and show us what they can do, and we want to show that in public)

To do that, we need to fork our project (chamilo/chamilo-lms) several times over.

So the first problem is: you cannot fork the same project twice with the same user.
Adrian Short (@adrianshort) kind of solved that issue in his blog article, saying that you can just do the fork manually, creating an empty repo on Github and filling it with a copy of the main repo, then adding the origin remote manually.

Even though that works, the Github page (starting from the second fork with the same user) will *not* indicate it has been forked from chamilo/chamilo-lms: you’ll have to issue the
git remote -v
command to see that the upstream is correct.

The second problem is that the article doesn’t dive into the details of branch-level forks, so this article intends to solve that particular missing detail, and in that respect, the Github help is definitely useful. Check Syncing a fork help page if you want the crunchy details.

To make it short, there are 3 issues here:

  • fetching the branches from the original repo (upstream)
  • getting the desired branch to be checked out into your local repo
  • defining that, from now on, you want to work on your repo as a fork of that specific branch in upstream (to be able to sync with it later on)

The complete procedure then, considering my personal account (ywarnier) and the original chamilo/chamilo-lms project on Github, with the intention to work on branch 1.9.x, would look like this:

git clone
git remote -v
git remote rename origin upstream

(here you have to create the “chamilo-lms-fork1” repo by hand in your Git account)

git remote add origin
git remote -v
git push -u origin master
git branch -va
git fetch upstream
git branch -va
git checkout --track remotes/upstream/1.9.x

This should get you up and kicking with your multiple forks in a relatively short time, hopefully!

%d bloggers like this: