PHP Composer Error - Cannot Redeclare Class

I rebuilt PHP last night to include APC and all of a sudden composer stopped working in my project. Whenever I ran a simple update, install or dump-autoload I’d get:

1
2
3
4
$ composer update
PHP Fatal error:  include(): Cannot redeclare class symfony\component\process\process in phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php on line 183

Fatal error: include(): Cannot redeclare class symfony\component\process\process in phar:///usr/local/bin/composer/vendor/composer/ClassLoader.php on line 183

A quick google told me others also had the same issue.

It seems that there’s a bug between composer.phar and APC. From one of the composer issues:

Quite weird, but then again the php docs seem to acknowledge that APC should not really be
enabled on the CLI. It seems to be a phar+apc bug, see https://bugs.php.net/bug.php?id=59398
https://bugs.php.net/bug.php?id=59829 https://bugs.php.net/bug.php?id=59907 as well.

There are a couple of things you can do to fix this, but they all involve turning off APC for php-cli. First, run composer diag to see whether any more information can be gleaned:

1
2
3
4
5
6
7
8
9
$ composer diag
Checking platform settings: FAIL

The apc.enable_cli setting is incorrect.
Add the following to the end of your `php.ini`:
    apc.enable_cli = Off

The php.ini used by your command-line PHP is: /opt/php-5.4.15/lib/php.ini
If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.

If you get that message, you know you have the same problem as I did :) You can also check the troubleshooting page on getcomposer.org

You can try running the self-update (for me I don’t reference the .phar file because I use the unix install convention).

1
php /usr/local/bin/composer self-update

Those are really just everyday things to try if you have composer issues, to really fix this you need to disable APC for php-cli. You should set this option to 0 in your php.ini

1
apc.enable_cli=0

If you don’t have access to php.ini, you can disable APC on the CLI per command:

1
php -d apc.enable_cli=0 /usr/local/bin/composer install

Note: if you have composer.phar in your project you could just do:

1
php -d apc.enable_cli=0 composer.phar install
php

Installing APC in PHP 5.4.x on Mac OSX

I recently installed PHP 5.4 from source but needed to add APC.

Most people can install this using PECL, but I got an error doing this:

1
2
3
4
5
6
7
$ pecl install apc

Warning: lstat(): Lstat failed for /private/tmp/pear/cache/497e483d585c1e3f341260e73a8c6e85rest.cacheid in PEAR/REST.php on line 276

Warning: lstat(): Lstat failed for /private/tmp/pear/cache/497e483d585c1e3f341260e73a8c6e85rest.cacheid in /opt/php-5.4.15/lib/php/PEAR/REST.php on line 276
No releases available for package "pecl.php.net/apc"
install failed

So instead I downloaded the APC source from the PECL website and ran the following:

1
2
3
4
5
6
cd APC-3.1.13/
phpize
./configure --with-php-config=/opt/php/bin/php-config --enable-apc
make
export TEST_PHP_ARGS='-n'
sudo make install

Note that my php-config setting is a little different because I installed my PHP into /opt instead of the default /usr/bin

The output of make install is:

1
2
3
$ sudo make install
Installing shared extensions:     /opt/php-5.4.15/lib/php/extensions/no-debug-non-zts-20100525/
Installing header files:          /opt/php-5.4.15/include/php/

Copy the Installing shared extensions path into your php.ini file.

1
2
3
4
5
6
7
[apc]
extension=/opt/php-5.4.15/lib/php/extensions/no-debug-non-zts-20100525/apc.so
apc.enabled=1
apc.shm_size=128M
apc.ttl=7200
apc.user_ttl=7200
apc.enable_cli=1

You can double check the installation process by either reviewing phpinfo(); in a browser or using CLI:

1
php -i | grep apc

If you see something like the following (from php -i)…all good:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
apc.cache_by_default => On => On
apc.canonicalize => On => On
apc.coredump_unmap => Off => Off
apc.enable_cli => On => On
apc.enabled => On => On
apc.file_md5 => Off => Off
apc.file_update_protection => 2 => 2
apc.filters => no value => no value
apc.gc_ttl => 3600 => 3600
apc.include_once_override => Off => Off
apc.lazy_classes => Off => Off
apc.lazy_functions => Off => Off
apc.max_file_size => 1M => 1M
apc.mmap_file_mask => no value => no value
apc.num_files_hint => 1000 => 1000
apc.preload_path => no value => no value
apc.report_autofilter => Off => Off
apc.rfc1867 => Off => Off
apc.rfc1867_freq => 0 => 0
apc.rfc1867_name => APC_UPLOAD_PROGRESS => APC_UPLOAD_PROGRESS
apc.rfc1867_prefix => upload_ => upload_
apc.rfc1867_ttl => 3600 => 3600
apc.serializer => default => default
apc.shm_segments => 1 => 1
apc.shm_size => 128M => 128M
apc.shm_strings_buffer => 4M => 4M
apc.slam_defense => On => On
apc.stat => On => On
apc.stat_ctime => Off => Off
apc.ttl => 7200 => 7200
apc.use_request_time => On => On
apc.user_entries_hint => 4096 => 4096
apc.user_ttl => 7200 => 7200
apc.write_lock => On => On
php

Base16 - Colour Scheme for Hackers

I came across a great set of colour schemes called base16. From the website:

Base16 provides carefully chosen syntax highlighting and a default set of 
sixteen colors suitable for a wide range of applications.

Base16 is both a color scheme and a template.

There are repos for the following tools:

  • Base 16 Builder
  • Vim
  • Shell
  • iTerm2
  • TextMate
  • OSX Color Palette
  • Xresources
  • Mou
  • XFCE4 Terminal
  • Gimp Palette
  • Gnome Terminal
  • Emacs
  • Geany

There’s even a builder so you can roll your own theme!

I’m currently using Monaki Dark 256 for iTerm2. The best bet is to git clone the iTerms 2 colour schemes to somewhere on your machine that is persistent. Perhaps ~/Documents/iterm2_schemes/base16.

Then you can experiment with the colours you like by going to preferences->profiles->colors then import your schemes using the load presets dropdown (pointing to wherever you cloned the base16 repo).

Monaki Dark 256

Installing Xdebug on Mac Mountain Lion (10.8.3)

Even though I’m running Mountain Lion (10.8.3) which comes with Xdebug I couldn’t get the PHP (5.4.14) local web server to register that Xdebug was actually installed and enabled.

My php.ini had the already installed Xdebug extension enabled:

1
zend_extension="/usr/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"

But whenever I inspected phpinfo() there wasn’t any mention of Xdebug.

Luckily the website has a wizard where you can paste the output of phpinfo(). From there you get detailed instructions on how to download, configure and make the latest version of Xdebug. It’s pretty simple.

Essentially the steps are as follows:

  1. Download xdebug-2.2.3.tgz
  2. Unpack the downloaded file with tar -xvzf xdebug-2.2.3.tgz
  3. Run: cd xdebug-2.2.3
  4. Run: phpize (Any problems…see the FAQ)
  5. Run: ./configure
  6. Run: make
  7. Run: sudo mkdir -p /usr/lib/php/extensions/no-debug-non-zts-20100525
  8. Run: sudo cp modules/xdebug.so /usr/lib/php/extensions/no-debug-non-zts-20100525
  9. Update your /etc/php.ini and uncomment/add the line zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
  10. Restart the webserver

Now if you check the output of phpinfo() you should see Xdebug.

To make sure your debug output is being styled properly, make sure you have html_errors = On in php.ini (it defaults to Off).

php

MySQL Thread_stack Overrun Error

We added a trigger to MySQL (v5.5.28) recently that was a simple UPDATE on table column, when a target table was UPDATEd. It ran fine (as expected) on our development environment but not when we pushed the changes to staging.

The error being thrown by MySQL was:

1
Thread stack overrun:  8304 bytes used of a 131072 byte stack, and 128000 bytes needed.  Use 'mysqld --thread_stack=#' to specify a bigger stack.

Querying the database told me that the current thread_stack setting is 128K:

1
2
3
4
5
6
7
mysql> show variables where `Variable_name` = 'thread_stack';
+---------------+--------+
| Variable_name | Value  |
+---------------+--------+
| thread_stack  | 131072 |
+---------------+--------+
1 row in set (0.00 sec)

This was confirmed by looking at the thread_stack variable in /etc/mysql/my.cnf

1
thread_stack = 128K

The docs for MySQL 5.5 suggest that it should be 192K for 32-bit systems and 256K for 64-bit systems. We’re running a 64-bit platform, you can tell by running:

1
2
[me@mydbserver ~]$ uname -m
x86_64

If the response is i686, you have a 32-bit version of Linux and if the response is x86_64 then you have a 64-bit version of Linux.

The fix is a simple case of increasing the value to 256K (if you’re on 64-bit) or 192K if you’re on 32-bit, as per the documentation. Don’t forget that the thread_stack is an allocation of memory per connection, so don’t set it too high or you might run into memory issues on your database server.

In case it wasn’t clear, you can make this change in your my.cnf file.

iOS 6 Caching POST Requests

Apparently I’ve been living under a rock, never having been bitten (that I know of) by the pretty massive bug Apple rolled out in iOS 6 (with Safari). The bug has been well documented around the interwebs, basically Safari caches HTTP POST requests. If you haven’t heard about this…stop and read that last bit again.

Now, replicating this bug is dependent on the payload of the POST not changing between requests, so in many circumstances you may be fine. However let’s talk about something like a simple login form.

  • User enters credentials in web form and clicks “Login”
  • App/site POSTs data to the server for authentication
  • Server authenticates request
  • 200 OK is returned, and a session is spawned on the server

Pretty standard workflow. Now what happens if the session expires and the user is presented with the login form again?

  • User enters credentials in web form and clicks “Login”
  • App/site POSTs data to the server for authentication
  • The request is cached because the users data (credentials) are the same as before

So in this scenario the request never actually gets sent to the server. Your app/site is now broken, the user cannot login. This sort of issue can of course have huge consequences for any number of websites/applications.

It’s worth noting that the HTTP/1.1 RFC states (thanks Diomidis Spinellis):

Responses to this method are not cacheable, unless the response includes appropriate Cache-Control or Expires header fields.

and:

Some HTTP methods MUST cause a cache to invalidate an entity. This is either the entity referred to by the Request-URI, or by the Location or Content-Location headers (if present). These methods are:

  • PUT
  • DELETE
  • POST

Solution

People have listed fixes like jQuery’s {cache: false} in the ajax params, to adding a random token to your payload. These methods seem a intrusive to me though as you need to change every form on every page in every app. So we went for a different approach, a simple Apache rule in your conf:

1
2
3
<Limit POST>
  Header set Cache-Control no-cache
</Limit>

All that’s saying is, if the incoming request is using the HTTP POST method, set a no-cache header. Nice and simple. Note that mod_headers needs to be enabled in Apache for this to work.

Migrating From Bitbucket to Github

Migrating repositories between bitbucket and github couldn’t be simpler thanks to the design nature of DVCS like git because the entire history is already located on your machine within your projects .git folder. All you’re really doing is changing a remote.

Open a terminal and navigate to your project directory.

TLDR;

For those who just want the commands with no explanation

1
2
3
4
git remote rename origin bitbucket
git remote add origin git@github.com:[username]/[repo_name].git
git push -u origin master
git remote rm bitbucket

Explanation of steps

Check which remote(s) you have now:

1
2
3
$ git remote -v show
origin    git@bitbucket.org:[username]/[repo_name].git (fetch)
origin    git@bitbucket.org:[username]/[repo_name].git (push)

Now rename your existing origin to bitbucket (or whatever you want to call it):

1
git remote rename origin bitbucket

Double check your change:

1
2
3
$ git remote -v show
bitbucket git@bitbucket.org:[username]/[repo_name].git (fetch)
bitbucket git@bitbucket.org:[username]/[repo_name].git (push)

Now add github as your origin and push the repository:

1
2
git remote add origin git@github.com:[username]/[repo_name].git
git push -u origin master

Check what remotes you have:

1
2
3
4
5
$ git remote -v show
bitbucket git@bitbucket.org:[username]/[repo_name].git (fetch)
bitbucket git@bitbucket.org:[username]/[repo_name].git (push)
origin    git@github.com:[username]/[repo_name].git (fetch)
origin    git@github.com:[username]/[repo_name].git (push)

Removed the old remote (bitbucket):

1
git remote rm bitbucket

Finally, check your remotes:

1
2
3
$ git remote -v show
origin    git@github.com:[username]/[repo_name].git (fetch)
origin    git@github.com:[username]/[repo_name].git (push)

Note that at the end of this, you still have your code on bitbucket, you’ve just pushed your repository to github and pointed your origin remote to there. To fully clean up you need to delete your repo from bitbucket.

git

Installing PHP 5.4+ From Source on Mac OSX Mountain Lion

Download and extract the PHP source from php.net, for me that was 5.4.14. Put it somewhere friendly, the desktop will do.

Open a terminal and install some dependancies via brew:

1
2
3
4
brew install libjpeg
brew install pcre
brew install libxml2
brew install mcrypt

Download and install ICU from http://site.icu-project.org/download/48#ICU4C-Download

1
2
3
4
5
tar xzvf icu4c-4_8_1-src.tgz
cd icu/source
./runConfigureICU MacOSX
make
sudo make install

Rebuild IMAP, download source from ftp://ftp.cac.washington.edu/imap/

1
2
3
4
make osx EXTRACFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp"
sudo cp c-client/*.h /usr/local/include/
sudo cp c-client/*.c /usr/local/lib/
sudo cp c-client/c-client.a /usr/local/lib/libc-client.a

Then in the PHP source folder you downloaded:

1
2
cd ~/Desktop/php/ext/imap
phpize

Now run configure (I installed with pear):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
./configure  \
--prefix=/usr  \
--mandir=/usr/share/man  \
--infodir=/usr/share/info  \
--sysconfdir=/private/etc  \
--with-apxs2=/usr/sbin/apxs  \
--enable-cli  \
--with-config-file-path=/etc  \
--with-libxml-dir=/usr  \
--with-openssl=/usr  \
--with-kerberos=/usr  \
--with-zlib=/usr  \
--enable-bcmath  \
--with-bz2=/usr  \
--enable-calendar  \
--with-curl=/usr  \
--enable-dba  \
--enable-exif  \
--enable-ftp  \
--with-gd  \
--enable-gd-native-ttf  \
--with-icu-dir=/usr/local  \
--with-iodbc=/usr  \
--with-ldap=/usr  \
--with-ldap-sasl=/usr  \
--with-libedit=/usr  \
--enable-mbstring  \
--enable-mbregex  \
--with-mysql=mysqlnd  \
--with-mysqli=mysqlnd  \
--with-pear  \
--with-pdo-mysql=mysqlnd  \
--with-mysql-sock=/var/mysql/mysql.sock  \
--with-readline=/usr  \
--enable-shmop  \
--with-snmp=/usr  \
--enable-soap  \
--enable-sockets  \
--enable-sysvmsg  \
--enable-sysvsem  \
--enable-sysvshm  \
--with-tidy  \
--enable-wddx  \
--with-xmlrpc  \
--with-iconv-dir=/usr  \
--with-xsl=/usr  \
--enable-zip  \
--with-imap=/usr/local/imap-2007 \
--with-kerberos \
--with-imap-ssl \
--enable-intl \
--with-pcre-regex  \
--with-pgsql=/usr  \
--with-pdo-pgsql=/usr \
--with-freetype-dir=/usr/X11 \
--with-jpeg-dir=/usr  \
--with-png-dir=/usr/X11

If you want to check for errors in the build step you can run:

1
make test

This might take a while, like 30 mins.

If you’re feeling bold, or once the test has completed, run:

1
sudo make install

Done!

PS If you’re getting strange errors during configure make sure you have these libraries:

1
2
3
brew install libpng
brew install freetype
brew install openssl

Change install path

The notes above install in the default /usr folder. If you want to install in /opt instead (which is often cleaner), add this to the configure script:

1
--prefix=/opt/php-5.4.14

Here’s a cutdown configure script I just used to install 5.4.15

1
'./configure'  '--prefix=/opt/php-5.4.15' '--with-libxml-dir=/usr' '--with-openssl=/usr' '--with-zlib=/usr' '--enable-bcmath' '--with-bz2=/usr' '--enable-calendar' '--with-curl=/usr' '--enable-dba' '--enable-exif' '--enable-ftp' '--with-gd' '--enable-gd-native-ttf' '--enable-mbstring' '--enable-mbregex' '--with-mysql=mysqlnd' '--with-mysqli=mysqlnd' '--with-pear' '--with-pdo-mysql=mysqlnd' '--with-mysql-sock=/var/mysql/mysql.sock' '--enable-sockets' '--enable-sysvmsg' '--enable-sysvsem' '--enable-sysvshm' '--with-tidy' '--with-xmlrpc' '--with-iconv-dir=/usr' '--with-xsl=/usr' '--enable-zip' '--enable-intl' '--with-pcre-regex' '--with-pgsql=/usr' '--with-pdo-pgsql=/usr' '--with-freetype-dir=/usr/X11' '--with-jpeg-dir=/usr' '--with-png-dir=/usr/X11' '--with-apxs2=/usr/sbin/apxs'
php

Date Timezone Error When Upgrading to PHP 5.3+

I upgraded to 5.4.14 last night on Mac OSX 10.8.3. Things went smoothly except I got an error running my apps:

date(): It is not safe to rely on the system’s timezone settings. You are required to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone ‘UTC’ for now, but please set date.timezone to select your timezone.

Basically since PHP 5.3.0 you have to explicitly set your date.timezone setting, this is a good thing. Prior to PHP 5.3.0, the line existed in the php.ini but was generally commented out. There are 2 places you could make this change.

  1. php.ini
  2. in your application codebase

Ideally you’d do both. Have a default in your php.ini that all applications on the server can use, and you can override if needed in your application.

php.ini

Look in your php.ini for the date.timezone setting and uncomment if necessary. For me I set it to “Australia/NSW”. You can get a full list of supported timezones here

1
date.timezone = "Australia/NSW"

Application codebase

The method above, of course is a server default, so if you want something specific to your application, try:

1
date_default_timezone_set("Australia/NSW");
php

Stopping a Remote Process After Killing an Ssh Session

I often want to tail log files on remote server(s) when running diagnostics for an application. You can either login to the remote server via an SSH session, then execute the tail command. Or you can do it from a local shell (without explicitly logging onto the remote server first). That’s pretty simple:

1
ssh myRemoteServer tail -f /path/to/logs/myapp.log

The problem here, is when you ctrl-c out of this command to kill the tail, the process is still running on the remote machine. Some googling told me that this is because of the lack of a controlling terminal for the running process.

From superuser.com:

This behaviour stems from the lack of a controlling terminal for the running process. When the remote process does not have a controlling terminal, the remote ssh process handling your session is unable to kill the command, which is left hanging in a zombie state to be eventually cleaned up by init.

So although the process on the remote server(s) will eventually be cleaned up, it’s not great to leave a lot of zombie processes lying around. And you certainly don’t want to logon to every server and ps ax to kill them. Crazy.

The answer, as described on superuser, is that you simply add the -t flag when you connect via SSH from a local terminal. Essentially that makes the remote process terminate when you ctrl+c your tail locally.

So for the initial example at the top:

1
ssh -t myRemoteServer tail -f /path/to/logs/myapp.log

I often use multitail a lot because it facilitates tailing log files on a remote servers from one command, a sample multi-tail script is now (with -t):

1
2
3
4
#!/bin/bash

multitail -l "ssh -t myRemote1 tail -f /path/to/logs/myapp.log" \
        -l "ssh -t myRemote2 tail -f /path/to/logs/myapp.log"