CSI: PHP

"Looking at your tweets I cannot even fathom what your job is. CSI:PHP?" — @grmpyprogrammer

Not Confusing Enough

| Comments

No, go ahead, add a little more abstraction. Don’t mind me, I’m just trying to maintain it is all.

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
<?php

class core_output
{

    static public function factory($output_engine, $module)
    {
        switch ($output_engine) {

            case 'smarty':
                $file = OUT_SMARTY;
                break;
            default:

                $file = OUT_SMARTY;

                break;
        }

        if (include($file)) {
            $class = 'core_output_' . $output_engine;
            if (class_exists($class)) {
                $presenter = new $class($module);
                if ($presenter instanceof core_output_common) {
                    return $presenter;
                }

                echo 'Invalid presentation class: ' . $output_engine;
            }

            echo 'Presentation class not found: ' . $output_engine;
        }

        echo 'Presenter file not found: ' . $output_engine;
    }

//end factory
}

No, Let’s Get ALL the Content at the Same Time!

| Comments

And let’s store it all in session! What’s more, if there’s a database error, let’s go ahead and let the users know about it with echo! Brilliant!

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
<?php

class BestCmsContentClassEvar
{
    protected function get_content()
    {
        $sql = 'select * from content order by id;';
        $result = $this->run_query($sql);
        //echo('looking for content');
        $i = 0;
        while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
            if ($row['location'] == 'inc') {
                $_SESSION['content_name'][$i] = $row['name'];
                $_SESSION['content_title'][$i] = $row['title'];
                $_SESSION['content'][$i] = 'inc';
            } else {
                $_SESSION['content_name'][$i] = $row['name'];
                $_SESSION['content_title'][$i] = $row['title'];
                $_SESSION['content'][$i] = $row['content'];
            }
            $i++;
        }
        echo(mysql_error());
        mysql_close();
    }
}

UPDATE: Thanks to @Rob_OEM for the image below. It gave me the lulz.

Go Ahead. Maintain This One. I Dare You.

| Comments

Page titles are stored in a numerically indexed session array. There are 22 cases in the switch statement and no default case. The style_inc property sometimes contains javascript, but not always.

Go ahead. Maintain this one. I dare you. I double-dog dare you.

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
<?php

class Page
{
    // . . .

    function set_style_title($page, $i)
    {
        switch ($page) {
            case 'home':
                $this->title = $_SESSION['content_title'][$i];
                $this->style_inc = '<link href="media/style/common.css" type="text/css" rel="stylesheet"></link>
                                    <link href="media/style/home.css" type="text/css" rel="stylesheet"></link>';
                break;
            case 'lost_pass':
                $this->title = $_SESSION['content_title'][$i];
                $this->style_inc = '<link href="media/style/common.css" type="text/css" rel="stylesheet"></link>
                                    <link href="media/style/lost.css" type="text/css" rel="stylesheet"></link>';
                break;
            case 'intro':
                $this->title = $_SESSION['content_title'][$i];
                $this->style_inc = '<link href="media/style/common.css" type="text/css" rel="stylesheet"></link>
                                    <link href="media/style/intro.css" type="text/css" rel="stylesheet"></link>';
                break;

            // . . . 19 more cases, but no default case . . .
        }
    }

    // . . .
}

Typical

| Comments

There’s nothing wrong with testing your application with dirty, ugly scripts like this. There is something wrong with dropping the test in the /includes folder, never deleting it, deploying it to production, and naming it editissues.php in an application where issue editing occurs.

1
2
3
4
5
6
7
8
9
10
11
12
<?php

$hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
$app = 'training_inc';
$to = "redacted@example.com";
$subject = "Hi!";
$body = "Hi,\n\nHow are you?\n\n$_SERVER[REMOTE_ADDR]\n$hostname\n app=$app";
if (mail($to, $subject, $body)) {
    echo("<p>Message successfully sent!</p>");
} else {
    echo("<p>Message delivery failed...</p>");
}

I Think I’m Going to Be Ill …

| Comments

Yup, I just threw up in my mouth a little.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

foreach ($this->models as $model) {
    $_file = '../' . $this->app . 'Models/' . $model . 'Model.php';
    $_class = $model . 'Model';

    if (file_exists($_file)) {
        require_once $_file;
        $this->{ucfirst($model)} = new $_class(null, dbConfig::$default);
    } else {
        $class = 'class ' . $_class . ' extends Model { }';
        eval($class);
        $this->{ucfirst($model)} = new $_class(null, dbConfig::$default);
    }
}

Source

Exception (Mis)handling

| Comments

Today’s post was submitted by an anonymous CSI: PHP investigator.

There’s some kind of design pattern going on here that I don’t quite understand, I’m certain, because this is all over the place:

1
2
3
4
5
6
7
8
9
10
11
<?php

try {
    // 20 - 100 lines of code...
} catch (Some_Application_Exception $e) {
    // TODO: Log Exception and re-raise...
} catch (Some_Framework_Exception $e) {
    // TODO: Log Exception and re-raise...
} catch (Exception $e) {
    // TODO: Log Exception and re-raise...
}

This isn’t the worst. Apparently, simply saving a code snippet to paste in with all those application and framework exceptions was getting tedious (never mind the cost of maintaining them), so this (d)evolved eventually into:

1
2
3
4
5
6
7
<?php

try {
    // 20 - 100 lines of code...
} catch (Exception $e) {
    // TODO: Log Exception and re-raise...
}

In the rare cases where the author was actually interested in the Exception thrown, we see lots of:

1
2
3
4
5
6
7
8
9
<?php

try {
    // 20 - 100 lines of code...
} catch (Exception $e) {
    if ($e instanceof Some_Very_Specific_Application_Exception) {
        // Log Exception. That is all.
    }
}

The backlog is peppered with White Screen of Death (WSOD) errors that have no diagnosis and few steps to reproduce… And then there are the Ajax requests that seemingly fail for no reason. If this was just in legacy code that could be traced to (read: blamed on) one or more former employees, that would be one thing, but it appears to be cultural at this point.

doBuildEditTableList

| Comments

My favorite parts are the relative links in to the stylesheet and the image. Write once, use only in a very specific subdirectory. Genius.

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
<?php

Function doBuildEditTableList($dbn){

    require "../config.inc";
    global $conn;

    $dbquery = "SELECT mytablename FROM updatetables ";
    mssql_select_db($dbn, $conn);
    $result = mssql_query($dbquery);
        print "<html><head>";
        print "<link rel=stylesheet type=text/css href='../$stylesheet' >";
        print "";
        print "</head><body>";
        print "<a href=$web><img align=center height=24 width=75 src=..$img/home.jpg alt='Return to Menu'></a>";
        print"<table class='form' align='center' width=75%>";
        print"<tr><td align='center' class='arow'>Edit Tables</td></tr>";
        print"<tr><td align='center' class='brow'>Select the table that you want to update:</td></tr></table>";
        print"<table class='form' align='center' width=75%>";
        While ($row = mssql_fetch_assoc($result)){
            foreach($row as $Key => $Value){ //foreach($row as $value)
                print "<tr><td align='center' class='form'><a class='blacklink' href=$script?req=list&table=$Value>$Value</a></td></tr>";
            }
        }
    print "</table></html>";
}

Designed to Be Called in the Middle of an SQL Query

| Comments

“… to simplify both the readability and repeatability of the query.” Yeah, I’m not sure it does that, especially when your function’s comments are TL;DR.

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
58
59
60
61
62
63
64
<?php

Function SQLMonthRange($field, $month=0, $formatOut="M",
                        $relative=true, $between=true) {
    /*
    * This is designed to be called in the middle of an SQL query
    * to simplify  both the readability and repeatability of the 
    * query.  The SQL query is assumed to be calling to capture 
    * values for a given field ($field) during 
    * the given month ($month).
    * 
    * A sample call might be:
    * 
    * $months =SQLMonthRange('COLUMN','11','M');
    * 
    * A sample output might be:
    * 
    * ( COLUMN BETWEEN '2007-Nov-01' AND '2007-Dec-01' )
    * 
    * when using the default $between = true, or
    * 
    * ( COLUMN >= '2007-Nov-01' and COLUMN < '2007-Dec-01' )
    * 
    * when using $default = false
    * 
    * $month is any valid (positive or negative) integer.  
    * In relative mode ($relative=true), 0 is the current month.
    * In absolute mode ($relative=false) 0 is December 
    * of the previous year
    *
    * RELATIVE MODE
    * $relative (true by default) allows us to keep a sliding month 
    * scale so that either positive or negative values reflect the 
    * current month plus the value of $month.  For example, if the 
    * present month is March, a $month value of 
    * -2 refers to January (two months prior to March), and a 
    * $month value of 5 refers to August (5 months after March).
    * 
    * Be careful in absolute mode (where $relative is false).  
    * In absolute mode, a $month value of -1 is NOT December.  
    * Remember that since 1 is January, it follows that 0 must 
    * be December (again, this example is for absolute mode).
    * 
    * $month =   0 ==> December of the previous year in absolute mode ($relative=false)
    * $month =  12 ==> December of the current year in absolute mode ($relative=false)
    * $month = -11 ==> January of the previous year in absolute mode ($relative=false)
    * 
    * $month =   0 ==> current month in relative mode ($relative=true)
    * $month =  12 ==> 12 months ahead of current month in relative mode ($relative=true)
    * $month = -11 ==> 11 months prior to current month in relative mode ($relative=true)
    *  
    * BETWEEN
    * $between allows us to switch to a BETWEEN statement or use 
    * the >= / <= combination, which may be useful when comparing 
    * speed tests.  Also, BETWEEN is inclusive of the beginning 
    * and ending values.  That is, it does in fact have an implicit 
    * ">=" at the beginning value and a "<=" at the ending value 
    * (not just a ">" and a "<").
    // http://msdn2.microsoft.com/en-us/library/aa225976(SQL.80).aspx

    */

    // ... snip ...
}

If you’re brave enough, or have enough time to waste, you can see the full function code here.

Use case:

1
2
3
4
<?

$q = "SELECT * FROM table WHERE SQLMonthRange "
        . SQLMonthRange('column', 7, 'm');

More Fun With Database Input Filtering

| Comments

And I thought prepString() was the pinnacle of awesome.

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
<?php

/* addslashes_mssql and stripslashes_mssql enable us to read from MS SQL Server 
(as opposed to other databases, where the PHP built-in functions addslashes() 
and stripslashes() work for MySQL and others
    */

function addslashes_mssql($str) {
    if (is_array($str)) {
        foreach ($str AS $id => $value) {
            $str[$id] = addslashes_mssql($value);
        }
    } else {
        $str = str_replace("'", "''", $str);
    }
    return $str;
}

function stripslashes_mssql($str) {
    if (is_array($str)) {
        foreach ($str AS $id => $value) {
            $str[$id] = stripslashes_mssql($value);
        }
    } else {
        $str = str_replace("''", "'", $str);
    }
    return $str;
}