Linux provides a lot of interesting command-line tools that we can use when working with PHP projects. In this post we give you some useful commands:

Find all PHP files in the current directory

$ find . -type f -name "*.php"
./index.php
./vendor/autoload.php

Obviously, we can use the same command to get another types of files, such as TWIG or JSON files:

$ find . -type f -name "*.twig"
./test.twig

$ find . -type f -name "*.json"
./composer.json

Check the syntax of all PHP files in the current directory

By itself, “php -l” checks the syntax of a single PHP file. This command can be really useful as part of a CI (Continuous Integration) system to check that we don’t have any syntax error in all PHP files of our project:

$ find . -type f -name "*.php" -exec php -l {} \;
No syntax errors detected in ./index.php
No syntax errors detected in ./vendor/autoload.php

There are another linters and validators that can be used the same way, but they require additional tools. For example, Composer has an option to validate the “composer.json” file, and Symfony provides linters for Twig and YAML files.

For example, if you have several “composer.json” files in your project and want to validate all of them, you can use the “composer validate” command (or “php composer.phar validate”) to check them all:

$ find . -type f -name "composer.json" -exec composer validate {} \;
./composer.json is valid for simple usage with composer but has
./vendor/symfony/config/Symfony/Component/Config/composer.json is valid
./vendor/symfony/console/Symfony/Component/Console/composer.json is valid
./vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/composer.json is valid

Get the size of each Composer dependency

How big are your dependencies? The du command estimates the file space usage for each dependency:

$ du -h -d 2 vendor
 56K	vendor/composer
496K	vendor/symfony/config
1.2M	vendor/symfony/console
...

Find suspicious PHP files

It’s very common in hacked WordPress projects to inject malicious code that makes use of functions such as eval(), exec() or base64_decode(). The following command finds PHP files that contain any of these functions:

$ find . -type f -name "*.php" -exec grep --with-filename "eval(\|exec(\|base64_decode(" {} \;
./virus.php: eval(base64_decode($_GET['a']));

Get the number of “use” statements in all PHP files, ordered

Does your classes contain too many “use” statements? Maybe it’s time to refactor! Check candidates with the following command:

$ find . -type f -name "*.php" -exec grep --with-filename -c "^use " {} \; | sort -t ":" -k 2 -n -r
./vendor/symfony/console/Symfony/Component/Console/Application.php:28
./vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php:21
./vendor/symfony/console/Symfony/Component/Console/Tests/ApplicationTest.php:18
./vendor/symfony/finder/Symfony/Component/Finder/Finder.php:15
...

Find files with abstract classes

The following command will find all files that define abstract classes:

$ find . -type f -name "*.php" -exec grep --with-filename -c "^abstract class " {} \; | grep ":[^0]"
./vendor/twig/twig/test/Twig/Tests/Profiler/Dumper/AbstractTest.php:1
./vendor/twig/twig/lib/Twig/TokenParser.php:1
...

Similarly, we can find PHP files containing 2 or more classes in a single file, ordered:

$ find . -type f -name "*.php" -exec grep --with-filename -c "^class " {} \; | grep ":[2-99]" | sort -t ":" -k 2 -n -r
./vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php:4
./vendor/twig/twig/test/Twig/Tests/IntegrationTest.php:4
...

List PHP settings for the xdebug extension

“php -i” outputs a huge list with all the settings that are in use. Thanks to the grep utility, we can display only the ones we are interested in. For example, to get all settings related to the xdebug extension:

$ php -i | grep xdebug

Find empty files and/or directories

Check if you left empty files in your project:

$ find . -type f -empty
./test.txt

List files currently open by a PHP process

In Linux, all processes are mapped into the /proc filesystem, so we can get lots of interesting information from a process, like open files.

For example, in the following script we open a file called “test.txt” located in the same directory of the file and then wait for 100 seconds so we have time to execute some commands.

$file = fopen(__DIR__ . 'test.txt', 'w');
sleep(100);
fclose($f);

Then, we execute it and get the PID (process id):

$ php test.php &
[2] 9525

$ ps aux|grep "test.php"
root      9525  0.1  2.0 161404 10736 pts/0    S    11:07   0:00 php test.php

So, the PID is 9525. Now, we can look for information about the process in the /proc filesystem. Each process has a directory with the same name as the PID. To get the list of open files:

$ ls -l /proc/9525/fd
total 0
lrwx------ 1 root root 64 Apr 23 11:07 0 -> /dev/pts/0
lrwx------ 1 root root 64 Apr 23 11:07 1 -> /dev/pts/0
lrwx------ 1 root root 64 Apr 23 11:07 2 -> /dev/pts/0
lr-x------ 1 root root 64 Apr 23 11:07 3 -> /proc/9525/auxv
l-wx------ 1 root root 64 Apr 23 11:07 4 -> /test.txt

The “fd” directory means file descriptors. And we can see that “test.txt” is there! The other file descriptors are for standard input, output and error.

We can also get the the full path of the command, so we know that has been executed using PHP 5.4:

$ ls -l /proc/9580/exe
lrwxrwxrwx 1 root root 0 Apr 23 11:11 /proc/9525/exe -> /usr/local/php54/bin/php

$ /usr/local/php54/bin/php -v
PHP 5.4.5 (cli) (built: Jul 26 2012 15:17:07)

Based on these commands, we can create new ones to fit our needs.