Monthly Archives: October 2019

PHP exec fails with rsync

PHP has an exec function which lets you run a program. Why would you want to use a PHP program to run a different program? You might want to run a file listing using ls, or copy files using cp, for example. When using exec, you need to be thoughtful of the environment in which PHP is running. A command line script that you run from a terminal, like php test.php, runs in a different environment than a php file run by Apache in responding to a web request. This post will take a look at the difference.

Suppose we have a little PHP file called whoami.php which runs the Linux whoami command:

<?php
  $result = exec("whoami", $output, $retVar);
  echo "Output:".print_r($output, TRUE)."\n";
  echo "retVar:".print_r($retVar, TRUE)."\n";
?>

When I run this as a script from the command line, php whoami.php, I see this output:

Output:Array
(
    [0] => fullstackdev
)

retVar:0

As expected, whoami returns fullstackdev – that’s my user name on my computer.

If I put this file into my Apache root directory and run it by surfing to http://localhost/whoami.php, I see something different in my browser:

Output:Array ( [0] => www-data ) retVar:0

If you have Apache up and running, you might want to try this for yourself.

At first, seeing this output may seem strange. Why does whoami return a different value when I run this script in the browser? The answer is that it’s not “me” running the script. It’s Apache, or more accurately, the “Apache user”. When Apache was installed on my system (Ubuntu 16.04), an Apache “user” was created which is responsible for running the Apache web server process. This user is named “www-data”.

You can also see who the Apache user is by listing the Apache processes from the command line with ps -ef|grep apache (or ps -ef|grep httpd). Here’s the output on my system, which again shows the Apache user being “www-data”:

ps -ef|grep apache
..
www-data 29172 29963  0 07:35 ?        00:00:00 /usr/sbin/apache2 -k start
...

This is something to keep in mind when comparing the results of a PHP script that you run in the browser versus one you run from the command line.

Usually, when running PHP scripts as Apache, it’s not relevant that “www-data” is running that script.

But there are cases where it does matter, and the PHP exec function can be one of them. For example, if your PHP script uses exec to ssh, rsync, or touch files in your own personal .ssh directory, you will probably run into trouble. The “www-data” user does not have access to your .ssh files. That user may not even have its own home directory, where .ssh would normally be located.

Here’s one final tip. If you’re trying to run commands that use passwordless ssh from Apache, you may want to rethink what you’re trying to do. There may be other ways to do what you want: cron jobs, or watch scripts.

Got comments? Send me an email at fullstackdev@fullstackoasis.com. If you found this interesting, you can hit the subscribe button above. I write a new post about once a week.

How to fix that PHP bug using error logging

Imagine you’ve got 20 lines of PHP code. There’s an UPDATE SQL statement deep within some nested logic statements. That UPDATE is just not working, even though you expect it to. You know that because you check the database, and the values in your table are not being updated.

The code looks like this:

<?php
...
if ($blah == 0) {
    ...
    $banana = !empty($_POST['banana']) ? $_POST['banana'] : '';
    $coconut = $_POST['coconut'];
    ...
    if (!empty($banana)) {
        $res = $db->query("SELECT ... SOME SQL THING");
        ...
        if ($res->num_rows > 0) {
            // avoid duplicates
            exit();
        } else {
            $insert = $db->query("INSERT INTO .... MORE SQL");

            if ($insert && !empty($coconut)) {
                $update = $db->query("UPDATE ... ANOTHER TABLE");
                // Gnash!! The UPDATE does not take place as expected!
                ...
// etc.
?>

How do you debug something like this? Here’s my process.

First, I look at the PHP logs. If error logging is enabled, it may be that some error is being logged which points to the problem right away. In that case, I might not have to do any further debugging. If I’m lucky, the fix is quick and easy! Maybe I’ll see some SQL error, like Undefined index or foreign key constraint fails, that makes the problem clear.

But suppose error logging is disabled. Then I enable it, at least for this script.

To do that, I add error_reporting(E_ALL); to the top of the script. Then, as a good test, I also add error_log('bananas'); to the top of the script, run the script, and check to make sure that my comment, “bananas”, was logged in the expected error log file. If it wasn’t, I’ll need to hunt down the file where errors are being logged! I won’t go into that here, though. Let’s assume I’ve found my error log, and I can print statements to it.

If I don’t see any errors in the log, I add a bunch of logging statements to figure out exactly where in the code things go wrong. Look at the code above, again. There are so many possible ways that the UPDATE statement might not have run – every logic branch needs to be tested to see where execution stops. If I expect that my UPDATE statement is definitely being run, I can log a short comment just before that, run the code, and check to see if that comment appears in the logs.

If my framework already has some form of logging enabled, great, I might just use that to add debugging comments. If not, that’s okay. As mentioned above, PHP comes with built-in error logging. I might add something like error_log("before UPDATE"), run the script, and check to see if my comment appears in the error log. If I don’t see “before UPDATE” in the logs, I’d probably add a bunch of error_log statements immediately after every branch in the logic: after each if and else statement in the code above.

Usually, by adding logging statements, I can quickly figure out where the problem occurs. Where does execution stop? Then, I add more debug statements to figure out why, maybe printing out variables.

A word of caution: before adding logging, I often copy the file that I’m going to be working on to a safe place. I do that something like this: cp -p myfile.php myfile.php.2019-10-21 (the -p option to cp preserves the file’s mode, permissions, and timestamps). This is true even if I’m working on code that is checked into a version control system such as git. It’s just a really quick way to be able to revert my changes, if needed.

There are other things that I might do to figure out what’s causing this bug, too. If the source code has a test framework in place, it can be super helpful to add tests which exercise the code. That way, I can isolate the buggy code outside of the website, and test if it works on its own. Usually, when a bug is reported, I do like to add at least one test which exhibits the error by failing, and which then passes when the bug is fixed. But I don’t always have the luxury of working with source code that has a test framework set up.

Note: I’m usually working in a Linux environment, so YMMV with any commands (cp) mentioned above.

Got comments? Send me an email at fullstackdev@fullstackoasis.com. I value your feedback! If you found this interesting, you can hit the subscribe button above. I post new content about once a week.

How to build your Android app without opening the IDE

When developing Android apps, I’m almost always working in the Android Studio development environment (IDE).

I have a pretty solid dev computer, but even so, it just takes a little while for Android Studio to start up. There are times when I have to make a teeny tiny change to the source code, and it’s just a chore to open the IDE, change the code, and rebuild my app.

For example, suppose my client has requested a change to a string that’s displayed in a TextView. I’d much rather open my strings.xml file using a text editor like vi, edit the string as requested, and rebuild the app without ever touching Studio.

In fact, it’s possible to do this. Open a terminal, and go to your Android Studio project root. For me, this is in my home directory under ~/StudioProjects/. After editing the strings file (e.g. ./app/src/main/res/values/strings.xml), run ./gradlew build. You’ll see a bunch of output like this:

./gradlew build
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
...
:app:test UP-TO-DATE
:app:check
:app:build

BUILD SUCCESSFUL

Total time: 4.855 secs

Your new apk will be in the default location for your project. If you don’t recall where that is, you can find it quickly by running find . -name "*.apk".

Note: this demo was done using Ubuntu 16.04 OS with Android Studio 2.3. I’m not sure if it will work for Android Studio 3, but I think it will.

Got comments? Send me an email at fullstackdev@fullstackoasis.com. I value your feedback! If you found this interesting, you can hit the subscribe button above. I post new content about once a week.

How to migrate PHP mysql to mysqli extension

Recently, I had a to migrate a website that was running PHP 5.6 over to PHP 7. As usual, the first thing I did was to just copy the site over to the new host, and check the logs to see what the errors were. I saw a ton of messages like this:

PHP Fatal error:  Uncaught Error: Call to undefined function mysql_connect()
...
PHP Fatal error:  Uncaught Error: Call to undefined function mysql_query()
...
PHP Fatal error:  Uncaught Error: Call to undefined function mysql_num_rows()
...

I was really lucky! The main problem was that the site relied on database extensions for MySQL that have been deprecated since PHP 5.5, and were removed in PHP 7.

This turned out to be a pretty easy fix. You can check out the documentation for the new MySQLi improved extension over at PHP.net. Here are the steps that I took to rework the code so that the site functioned normally, again.

mysql_connect and mysql_select_db

The previous connection method was mysql_connect, called like this:

$conn = mysql_connect(mysql_server_name, username, password);

I replaced that with this:

$conn = mysqli_connect(mysql_server_name, username, password, database_name);

Since I could pass in the database name, I could then throw out an old call to mysql_select_db as well.

mysql_real_escape_string

mysql_real_escape_string($str) was replaced with $conn->real_escape_string($str). I could have also used mysqli_real_escape_string($str).

mysql_query

$results = mysql_query($sql_query) was replaced with $results = $conn->query($sql_query).

mysql_num_rows

mysql_num_rows($results) was replaced with $results->num_rows.

mysql_fetch_assoc

$row = mysql_fetch_assoc($results) was replaced with $results->fetch_assoc().

mysql_error

$err = mysql_error($conn) was replaced with $err = $conn->error.

mysql_close

mysql_close($conn) was replaced with $conn->close().

Aside from these problems with using the old MySQL extension, I noticed one other problem. There were a few messages in the PHP logs, like this:

PHP Notice:  Undefined variable: blah

This happened when the variable had never been defined before it was used. For example, I saw code like this:

$myArr = getSomething($blah);
...

The variable $blah was only found once in that source code – it had never been initialized. So I just initialized it to NULL, and the notice went away. Probably it was a typo or copy/paste error. I also double-checked to make sure that NULL was an acceptable input, and not a lurking bug.

The PHP notice message didn’t do much harm, but it was cluttering up the logs, making it hard to see the real problems, so getting rid of it was a good thing. As a general rule, I like to get rid of messages like this. That way, if something goes wrong, it’s easier to find new problems by checking the logs.