Archive

Archive for the ‘Computing’ Category

SOAP HTTP/1.1 500 Internal Service Error

August 5th, 2010 No comments

When you get a HTTP/1.1 500 Internal Service Error http header while using the PHP  SoapServer->handle() method as response, while all other output seems fine: Check if the request handler $soapserver->handle() is called only once. Multiple method calls on this method can produce this error if the first handling generates a service error, and the second call does not.

If you need the SOAP headers for processing or passing to the operation object before handling the request, use $request = file_get_contents(“php://input”) instead of $request = $soapserver->getLastRequest(). It seems $soapserver->getLastRequest() can’t be called before you call $soapserver->handle().

This will also make your webservice considerably faster for all operations for which SoapServer::handle() is called twice.

Categories: PHP Tags:

A simple error handler for PHP

December 25th, 2009 No comments

What is the best way to handle your PHP application errors? There is no single good answer to that question. It all depends on how your application works, what environment the application runs in and also how maintenance is performed.

But one thing is the same for every application. You might already know that showing errors in your application to clients is not a very good practice. It’s a security risk because users can learn about the internals of your server and your application. Besides that, PHP error messages do not mean anything to a user of your application so it is pointless to show them what is wrong internally.
Alternatively you should use try and catch constructs to show friendly error messages to the users on the client side of your application if errors should be displayed to the user when they occur.

1
2
3
try {
} catch() {
}

You can use trigger_error() with the predefined error types E_USER_* to trigger your own application errors. For example to trigger PHP errors when your application encounters a MySQL error:

1
2
$query = 'SELECT YEAR(NOW())';
$result = mysql_query($query) or trigger_error(mysql_error().' - '.$query, E_USER_ERROR));

This way MySQL errors will be visible for you, but you do not give away anything about your database model to clients.
To see which predefined error constants there are available, see: http://www.php.net/manual/en/errorfunc.constants.php. A useful thing to note is that triggering notes or warnings does not stop the script from executing further. If you need to break the script and stop it from continuing, trigger an error.

So what can you do to get notified of PHP errors yourself, without having to keep up with the apache error log while not displaying any errors in the browser on the client side on your production application?
I’ve created a simple error handler that can replace or accompany the default PHP error handler.
This is a useful way of getting notified of errors, because you will get the errors in your mailbox directly when the error occurs. So you will know an error has occurred before a user can even send a bug report. This is not a solution for all situations, but it can sure be useful in some cases to get notified of errors you would otherwise miss, or would not be reported by users. Another useful feature is that you will receive the state of variables at the moment the error occurs. This can be immensely useful for debugging.

Here is the full error handler file (error_handler.php). You can put it into a PHP include directory so you can include it anywhere, or you can put it in one of the directories of your application.

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<?php
/*
 * Just a simple error handler.
 * It sends mails now, but can easily be modified to store the errors in a database instead of sending mails.
 * 
 * It optionally requires a database table for error checksums, this way you only get each error only once in your mailbox.
 * 
 * It's not very prety yet, but it is functional.
 * 
 * @author Oxidiser
 * @version 2.0
 * 
CREATE TABLE `error_checksums` (
  `checksum` varchar(32) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`checksum`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 */
// Mail where error e-mails should end up.
define('ERROR_REPORT_EMAIL', "someemailaddress@somedomain.com");
// Define if the original PHP error handler should also handle the error.
define('PHP_ERROR_HANDLER_ACTIVE', false);
// Should the error handler use checksums to prevent sending dulicate error messages?
define('USE_DATABASE', false);
// MySQL db options
define('MYSQL_HOST', "localhost");
define('MYSQL_USER', "");
define('MYSQL_PASSWORD', "");
define('MYSQL_DATABASE', "");
// Never display any errors to clients.
ini_set('display_errors', 0);
 
// Handle every error type. Good for test enviroment.
error_reporting(E_ALL);
// Handle every error type except notices and strict warnings. Good for the production enviroment.
//error_reporting(E_ALL & ~ E_NOTICE | E_STRICT);
 
// If error handler is not yet registered.
if(!is_callable('lookup_ErrorHandler')) {
    /**
     * Fetches an instance of the ErrorHandler object and calls the handleError() method;
     *
     * @param integer $errNo Contains the level of the error raised, as an integer.
     * @param string $errMsg Contains the error message, as a string.
     * @param string $fileName Contains the filename that the error was raised in, as a string.
     * @param integer $lineNum Contains the line number the error was raised at, as an integer.
     * @param array $vars An array that points to the active symbol table at the point the error occurred. In other words, errcontext will contain an array of every variable that existed in the scope the error was triggered in. User error handler must not modify error context.
     * @return void
     * 
     */
    function lookup_ErrorHandler ($errNo, $errMsg, $fileName, $lineNum, $vars)
    {
        // Run trough all error types.
        // To disable one of the error types, just comment out that case.
        // It might be useful to disable the notice error levels for sloppy programmers for instance.
        switch($errNo) {
            case E_ERROR:
            case E_WARNING:
            case E_PARSE:
            case E_USER_ERROR:
            case E_USER_WARNING:
            case E_STRICT:
            case E_COMPILE_ERROR:
            case E_COMPILE_WARNING:
            case E_CORE_ERROR:
            case E_CORE_WARNING:
            case E_RECOVERABLE_ERROR:
            case E_NOTICE:
            case E_USER_NOTICE:
                ErrorHandler::getInstance(ERROR_HANDLER_SUBJ_APPEND)->handleError($errNo, $errMsg, $fileName, $lineNum, $vars);
                break;
            default:
                // Error type should not be handled. Do nothing.
                break;
        }
        // Do or don't execute PHP internal error handler, return false if it should execute the PHP error handler, true if it should not.
        return !PHP_ERROR_HANDLER_ACTIVE;
    }
}
/**
 * This class replaces the default PHP error handler.
 * 
 * @param string $subjAppend This should contain the prefix for the e-mail subject. Useful for sorting e-mails by website when using the same error handler, when you for instance, place the error handler in an include directory. 
 * @version 1.0
 * @author Oxidiser
 * 
 */
class ErrorHandler
{
    /*
     * @var mixed (array with strings containing every error encountered during script execution)
     */
    public $errorsARR;
    /*
     * @var string A variable trace.
     */
    public $vars;
    /*
     * @var string (containing a string to prepend the error subject e-mail)
     */
    public $subjectAppend;
    /*
     * @var ErrorHandler
     */
    private static $instance;
    /*
     * @var MySQL link identifier
     */
    protected $_errorDb;
    /**
     * Create a new instance of the ErrorHandler object.
     * 
     * @param string $subjAppend The string that should be appended to the subject.
     * @return void
     */
    private function __construct($subjAppend)
    {
        $this->errormail = ERROR_REPORT_EMAIL;
        $this->subjectAppend = $subjAppend;
        $this->errortype = array(E_ERROR => 'Error',
                                 E_WARNING => 'Warning',
                                 E_PARSE => 'Parsing Error',
                                 E_NOTICE => 'Notice',
                                 E_CORE_ERROR => 'Core Error',
                                 E_CORE_WARNING => 'Core Warning',
                                 E_COMPILE_ERROR => 'Compile Error',
                                 E_COMPILE_WARNING => 'Compile Warning',
                                 E_USER_ERROR => 'User Error',
                                 E_USER_WARNING => 'User Warning',
                                 E_USER_NOTICE => 'User Notice',
                                 E_STRICT => 'Runtime Notice',
                                 E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
                                );
        $this->errorsARR = array();
        if(USE_DATABASE) {
            // Database should be used, connect to MySQL instance.
            $this->_errorDb = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD);
            // Select the apropriate database.
            mysql_select_db(MYSQL_DATABASE, $this->_errorDb);
        }
    }
    /**
     * Clone the object (required for the singleton)
     * 
     * @return void
     */
    private function __clone ()
    {}
    /**
     * Returns the instance of the ErrorHandler object.
     *
     * @param string $subjAppend This should contain the prefix for the e-mail subject. Usefull for sorting e-mails by website.
     * @return ErrorHandler
     */
    public static function getInstance ($subjAppend)
    {
        if(!ErrorHandler::$instance instanceof self) {
            ErrorHandler::$instance = new self($subjAppend);
        }
        return ErrorHandler::$instance;
    }
    /**
     * This mehtod actually handles the errors recieved from the PHP core.
     *
     * @param integer $errNo Contains the level of the error raised, as an integer.
     * @param string $errMsg Contains the error message, as a string.
     * @param string $fileName Contains the filename that the error was raised in, as a string.
     * @param integer $lineNum Contains the line number the error was raised at, as an integer.
     * @param array $vars An array that points to the active symbol table at the point the error occurred. In other words, errcontext will contain an array of every variable that existed in the scope the error was triggered in. User error handler must not modify error context.
     * @return void
     * 
     */
    public function handleError($errNo, $errMsg, $fileName, $lineNum, $vars)
    {
        if(USE_DATABASE) {
            // Use database for checksums.
            // Check checksum in the hope of reducing server load by only mailing errors once.
            $errorChecksum = md5($errNo . $errMsg . $fileName . $lineNum);
            // Check if checksum exists in database.
            // @todo What is faster? Check with a select statement like it does now, or only do an insert query and check for an exception (mysql error, primairy key already exists)?
            $q = mysql_query("SELECT checksum FROM error_checksums WHERE checksum = '{$errorChecksum}'", $this->_errorDb) or trigger_error('Something went wrong with the error handler checksum check query: '.mysql_error(), E_USER_ERROR);
            if(mysql_num_rows($q) > 0) {
                // Error already mailed. Return without adding error to array in object and adding checksum to the database.
                return 0;
            }
            // Add checksum to database.
            mysql_query("INSERT INTO error_checksums (checksum) VALUES ('{$errorChecksum}')", $this->_errorDb) or trigger_error('Something went wrong with the error handler checksum insert query: '.mysql_error(), E_USER_ERROR);
        }
        // Get vars from thrown error.
        $this->vars = $vars;
        // Get the current date.
        $dt = date("Y-m-d H:i:s (T)");
        // Get the next empty key (auto increment).
        $nextKey = count($this->errorsARR);
        // get defined vars.
        $this->errorsARR[$nextKey]['date'] = $dt;
        $this->errorsARR[$nextKey]['errno'] = $errNo;
        $this->errorsARR[$nextKey]['errtype'] = $this->errortype[$errNo];
        $this->errorsARR[$nextKey]['errmessage'] = $errMsg;
        $this->errorsARR[$nextKey]['script'] = $fileName;
        $this->errorsARR[$nextKey]['url'] = 'http://'.$_SERVER['SERVER_NAME'].($_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : ($_REQUEST['SCRIPT_NAME'] ? $_REQUEST['SCRIPT_NAME'] : ($_ENV['SCRIPT_NAME'] ? $_ENV['SCRIPT_NAME'] : ($PHP_SELF ? $PHP_SELF : 'oopsnoscript')))) . ($_SERVER['QUERY_STRING'] ? '?' . $_SERVER['QUERY_STRING'] : '');
        $this->errorsARR[$nextKey]['lineno'] = $lineNum;
        // Get the state of the superglobals at the point of the error.
        $this->errorsARR[$nextKey]['session'] = $_SESSION;
        $this->errorsARR[$nextKey]['request'] = $_REQUEST;
        if(USE_DATABASE) {
            $this->errorsARR[$nextKey]['checksum'] = $errorChecksum;
        }
        if(isset($_SERVER['REMOTE_ADDR'])) {
            // Remote address is set, so store the client IP.
            $this->errorsARR[$nextKey]['user'] = $_SERVER['REMOTE_ADDR'];
        } else {
            // Unknown remote address.
            $this->errorsARR[$nextKey]['user'] = "Unknown";
        }
        // Extract the filename.
        $tmpArr = explode('/', $fileName);
        $errorFileName = array_pop($tmpArr);
        // Set the subject for this errors e-mail.
        $this->errorsARR[$nextKey]['subject'] = $this->errortype[$errNo] . ' in ' . $errorFileName. ' line #' . $lineNum;
    }
    /**
     * Destructor for the object. Sends (unique if checksums are used) errors once for each script execution at the end of execution.
     *
     * @return void
     */
    function __destruct ()
    {
        // Check if any errors were triggered during execution.
        if (count($this->errorsARR) > 0) {
            // Set headers for error mails.
            $headers = "From: \"somesite.nl Error reporter\" <errorhandler@somesite.nl>\n";
            $headers .= "Return-Path: <errorhandler@somesite.nl>\n";
            $headers .= "MIME-Version: 1.0\n";
            $headers .= "Content-Type: text/HTML; charset=ISO-8859-1\n";
            // Go through each error that was encountered.
            foreach($this->errorsARR as $err) {
                // Create content for error mail. 
                $content = "<table width=\"100%\" style=\"border:1px solid;\">";
                $content .= "<tr><td style=\"border:1px solid; background-color:#9FC2FF;\" colspan=\"2\"><strong>Message & Vitals</strong></td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>Error #</strong></td><td style=\"border:1px solid;\">{$err['errno']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>Error type</strong></td><td style=\"border:1px solid;\">{$err['errtype']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>Error message</strong></td><td style=\"border:1px solid;\">{$err['errmessage']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>Date & time</strong></td><td style=\"border:1px solid;\">{$err['date']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>URL</strong></td><td style=\"border:1px solid;\">{$err['url']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>File</strong></td><td style=\"border:1px solid;\">{$err['script']}</td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\"><strong>Line</strong></td><td style=\"border:1px solid;\">{$err['lineno']}</td></tr>";
                if(USE_DATABASE) {
                    $content .= "<tr><td style=\"border:1px solid;\"><strong>Error checksum</strong></td><td style=\"border:1px solid;\">{$err['checksum']}</td></tr>";
                }
                $content .= "<tr><td style=\"border:1px solid; background-color:#9FC2FF;\" colspan=\"2\"><strong>\$_SERVER</strong></td></tr>";
                // Format the server superglobal.
                foreach($_SERVER as $key=>$val) {
                    $content .= "<tr><td style=\"border:1px solid;\"><strong>{$key}</strong></td><td style=\"border:1px solid;\">{$val}</td></tr>";
                }
                $content .= "<tr><td style=\"border:1px solid; background-color:#9FC2FF;\" colspan=\"2\"><strong>\$_REQUEST</strong></td></tr>";;
                // Format the request superglobal.
                foreach($err['request'] as $key=>$val) {
                    $content .= "<tr><td style=\"border:1px solid;\"><strong>".print_r($key, true)."</strong></td><td style=\"border:1px solid;\"><pre>".print_r($val, true)."</pre></td></tr>";
                }
                $content .= "<tr><td style=\"border:1px solid; background-color:#9FC2FF;\" colspan=\"2\"><strong>\$_SESSION</strong></td></tr>";;
                // Format the session superglobal.
                foreach($err['session'] as $key=>$val) {
                    $content .= "<tr><td style=\"border:1px solid;\"><strong>".print_r($key, true)."</strong></td><td style=\"border:1px solid;\"><pre>".print_r($val, true)."</pre></td></tr>";
                }
                $content .= "<tr><td style=\"border:1px solid; background-color:#9FC2FF;\" colspan=\"2\"><strong>Stack trace</strong></td></tr>";
                $content .= "<tr><td style=\"border:1px solid;\" colspan=\"2\"><pre>".print_r($this->vars, true)."</pre></td></tr>";
                $content .= "</table>";
                // Dispatch the error mail.
                mail($this->errormail, $this->subjectAppend.' '.$err["subject"], $content, $headers);
            }
        }
        // Restore the deafult PHP error handler.
        restore_error_handler();
        // Restore the default PHP config value.
        ini_restore('display_errors');
    }
}
// Set the custom error handler to handle the errors.
set_error_handler('lookup_ErrorHandler');

Here is a small file with which you can test the error handler:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
define("ERROR_HANDLER_SUBJ_APPEND", 'WEBSITE IDENTIFIER');
// You can use this error handler by simply including it in your code. Preferably as high in the execution chain as possible.
// Include error handler from include path dir.
//include_once("error_handler.php");
// Include with absolute path.
include_once(dirname(__FILE__)."/error_handler.php");
 
session_start();
$_SESSION['justatest'] = array('anarraykey' => 'withanarrayvalue');
 
trigger_error('This is just a test.', E_USER_WARNING);
Categories: PHP Tags: , , ,

Using a custom view helper in Zend Framework to create breadcrumbs

December 15th, 2009 1 comment

I don’t think I am the only one who needs to put breadcrumbs on a page with zend framework. So I’ll share my helper with you. You can use this helper in you views to set the breadcrumbs.

You can use this helper like you use the HeadTitle helper, it is based on that.
So, in you layout, select which separator you want to use, and if you want to use automatic escaping or not (Can’t use this if you use urls). You can also set a prefix, postfix and indent if you want, just like with the HeadTitle helper. You can set, append and prepend items to the breadcrumb trail.

This is the code for the layout and also an example of prepending an item to the breadcrumb:

1
2
3
4
5
6
7
8
9
10
  <?php
    // Set the separator for the breadcrumbs.
    $this->breadCrumb()->setSeparator(' > ');
    // Disable the auto escaping of html.
    $this->breadCrumb()->setAutoEscape(false);
    // Set the first (default) element of the breadcrumbs.
    $this->breadCrumb("Home", "/", Zend_View_Helper_Placeholder_Container_Abstract::PREPEND);
    // Display the breadcrumb.
    echo $this->breadCrumb();
    // ...

This is an example of the code for a view script:

1
2
3
4
5
<?php
// Set breadcrumbs for this view.
$this->breadCrumb($this->placeholder('category_name'), $this->url(array('category' => $this->placeholder('category_url_name')), 'category_route', true));
$this->breadCrumb($this->placeholder('subject_name'));
// ...

The full helper:

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<?php
/** Zend_View_Helper_Placeholder_Container_Standalone */
require_once 'Zend/View/Helper/Placeholder/Container/Standalone.php';
 
/**
 * Helper for setting and retrieving breadcrumbs for HTML.
 * Based on the HeadTitle helper.
 *
 * @version 1.0
 * @uses Zend_View_Helper_Placeholder_Container_Standalone
 */
class Zend_View_Helper_BreadCrumb extends Zend_View_Helper_Placeholder_Container_Standalone
{
    /**
     * Registry key for placeholder
     * @var string
     */
    protected $_regKey = 'Zend_View_Helper_BreadCrumb';
 
    /**
     * Whether or not auto-translation is enabled
     * @var boolean
     */
    protected $_translate = false;
 
    /**
     * Translation object
     *
     * @var Zend_Translate_Adapter
     */
    protected $_translator;
 
    /**
     * Container for the urls of the items.
     * 
     * @var Zend_View_Helper_Placeholder_Container
     */
    protected $_urls;
 
    /**
     * Retrieve placeholder for title element and optionally set a new item.
     *
     * @param  string $title
     * @param  string $url
     * @param  string $setType
     * @param  string $separator
     * @return Zend_View_Helper_BreadCrumb
     */
    public function breadCrumb($title = null, $url = null, $setType = Zend_View_Helper_Placeholder_Container_Abstract::APPEND)
    {
        // Initialize the url placeholder container if it is not created yet.
        if(!is_object($this->_urls)) $this->_urls = new Zend_View_Helper_Placeholder_Container();
        if ($title) {
            // Title must be given.
            if ($setType == Zend_View_Helper_Placeholder_Container_Abstract::SET) {
                // Reset the objects and set the first item.
                $this->set($title);
                $this->_urls->set($url);
            } elseif ($setType == Zend_View_Helper_Placeholder_Container_Abstract::PREPEND) {
                // Prepend an item to the breadcrumb.
                $this->prepend($title);
                $this->_urls->prepend($url);
            } else {
                // Append an item to the breadcrumb.
                $this->append($title);
                $this->_urls->append($url);
            }
        }
        // Return self for chaining and display of the breadcrumbs.
        return $this;
    }
 
    /**
     * Sets a translation Adapter for translation
     *
     * @param  Zend_Translate|Zend_Translate_Adapter $translate
     * @return Zend_View_Helper_BreadCrumb
     */
    public function setTranslator($translate)
    {
        if ($translate instanceof Zend_Translate_Adapter) {
            $this->_translator = $translate;
        } elseif ($translate instanceof Zend_Translate) {
            $this->_translator = $translate->getAdapter();
        } else {
            require_once 'Zend/View/Exception.php';
            throw new Zend_View_Exception("You must set an instance of Zend_Translate or Zend_Translate_Adapter");
        }
        return $this;
    }
 
    /*
     * Retrieve translation object
     *
     * If none is currently registered, attempts to pull it from the registry
     * using the key 'Zend_Translate'.
     *
     * @return Zend_Translate_Adapter|null
     */
    public function getTranslator()
    {
        if (null === $this->_translator) {
            require_once 'Zend/Registry.php';
            if (Zend_Registry::isRegistered('Zend_Translate')) {
                $this->setTranslator(Zend_Registry::get('Zend_Translate'));
            }
        }
        return $this->_translator;
    }
 
    /**
     * Enables translation
     *
     * @return Zend_View_Helper_BreadCrumb
     */
    public function enableTranslation()
    {
        $this->_translate = true;
        return $this;
    }
 
    /**
     * Disables translation
     *
     * @return Zend_View_Helper_BreadCrumb
     */
    public function disableTranslation()
    {
        $this->_translate = false;
        return $this;
    }
 
    /**
     * Turn helper into string to echo.
     *
     * @param  string|null $indent
     * @param  string|null $locale
     * @return string
     */
    public function toString($indent = null, $locale = null)
    {
        $indent = (null !== $indent) ? $this->getWhitespace($indent) : $this->getIndent();
 
        $items = array();
        if($this->_translate && $translator = $this->getTranslator()) {
            // Translator is set.
            for($i = 0; $i < $this->count(); $i++) {
                // Loop through all breadcrumb items.
                // Get item and its url.
                $item = $this->offsetGet($i);
                $url = $this->_urls->offsetGet($i);
                if(null === $url) {
                    // No url is set, add an url-less item.
                    $items[] = $translator->translate($item, $locale);
                } else {
                    // Url is set, create HTML.
                    $items[] = '<a href="'.$url.'">'.$translator->translate($item, $locale).'</a>';
                }
            }
        } else {
            // No translation required.
            for($i = 0; $i < $this->count(); $i++) {
                // Loop through all breadcrumb items.
                // Get item and its url.
                $item = $this->offsetGet($i);
                $url = $this->_urls->offsetGet($i);
                if(null === $url) {
                    // No url is set, add an url-less item.
                    $items[] = $item;
                } else {
                    // Url is set, create HTML.
                    $items[] = '<a href="'.$url.'">'.$item.'</a>';
                }
            }
        }
        // Fetch set separator.
        $separator = $this->getSeparator();
        $output = '';
        // Check if a prefix was set.
        if(($prefix = $this->getPrefix())) {
            // Add prefix to beginning of string.
            $output  .= $prefix;
        }
        // Implode all items and append them to the string.
        $output .= implode($separator, $items);
        // Check if a postfix was set.
        if(($postfix = $this->getPostfix())) {
            // Add a postfix to the end of the string.
            $output .= $postfix;
        }
        // Check if escaping is enabled (should be disabled when using urls).
        $output = ($this->_autoEscape) ? $this->_escape($output) : $output;
        // Return the final string with breadcrumbs.
        return $indent . $output;
    }
}

PHP Security – Disabling potentially harmful functions

September 3rd, 2009 No comments

PHP Security – Disabling potentially harmful functions

Most instances of hacked PHP web-servers I’ve seen have been exploited by some hole in an application or some other way to upload bad PHP code. These PHP scripts run malicious code through the PHP functions that execute commands directly on the server. This can easily be countered by disabling those functions.

Just use the disable_functions configuration option in your global php.ini file:

disable_functions = "apache_child_terminate, apache_setenv, define_syslog_variables, escapeshellarg, escapeshellcmd, exec, fputs, fwrite, highlight_file, passthru, php_uname, popen, posix_getpwuid, posix_kill, posix_mkfifo, posix_setpgid, posix_setsid, posix_setuid, posix_setuid, posix_uname, proc_close, proc_get_status, proc_nice, proc_open, proc_terminate, shell_exec, system, eval"

You cannot set the disable_functions through set_ini() (see: http://www.php.net/manual/en/ini.list.php). So the PHP configuration modifying functions can be left enabled without risk. The code above also blocks some other potentially harmful functions.

Categories: PHP Tags: , , ,

How to add a subversion build number to your Xcode project

August 19th, 2009 No comments
Major version and SVN revision number used as build number

Major version and SVN revision number used as build number

Using the SVN revision number can be very useful. For example when publishing beta versions, or just being able to track your builds in more detail.

Create an empty  file called “buildnumber.xcconfig” in you project resource folder.

Double click your build target. Go to the build tab. in the dropdown on the bottom right, select Based On: buildnumber.
Scroll down and under the header Versioning change the Current Project Version to ${BUILD_NUMBER}.

Open your Info.plist file. Add these lines:
<key>CFBundleVersion</key>
<string>${BUILD_NUMBER}</string>
<key>CFBundleShortVersionString</key>
<string>Version MAJORVERSION</string>

You can enter anything you want for CFBundleVersion. This will be the major version name.
Now expand your target until you see Run Script. Right click it and go to Add>New Build Phase>New Run Script Build Phase.
Enter this script in the script window and change the project directory to match yours.

export TEMP=`/usr/bin/svnversion -nc ${PROJECT_DIR} | /usr/bin/sed -e ‘s/^[^:]*://;s/[A-Za-z]//’`
echo “BUILD_NUMBER = $TEMP” > ${PROJECT_DIR}/buildnumber.xcconfig

After this you should be getting “Version MAJORVERSION (xx)” string in your application’s about window with xx being the current SVN revision. You might need to clean all targets and rebuild your application for the changes to kick in.

Creating and transfering pdb ebook files for use on the iphone/ipod touch eReader application

August 2nd, 2009 No comments

I really love the eReader application on my iPod touch. I think it is the best application that I’ve come across uptill now to read books. But you can’t just drag and drop e-books to your iPhone or iPod touch because apple locks iPhone applications to only their own storage area to store and retrieve data. This is one of the hassles besides converting e-books to the required pdb palm os file format. This is a short post on how to get your e-books from your computer to your eReader application on your iPhone or iPod touch.

The first step is to convert your e-books to the pdb format required by eReader. I use PorDiBle for OS X to convert plain text  (.txt), ms word (.doc), HMTL (.html, .htm) and Rich text (.rtf) formats to .pdb. You can get it here: http://www.loghound.com/pordible/. When installed, just drag & drop the .txt file onto the application icon and it will prompt you to enter the books name. After this the application will convert it to a .pdb file in the same directory and with the same filename as the original. If you want to delete the source file automatically after converting, just open PorBiBle and select preferences from the menu.
But you’ll often have e-books in different formats. For example, the two most important: PDF and microsoft’s lit.
These you’ll have to convert to plain text before you can drop them into PorDiBle. To do that for PDF, just open the file in adobe acrobat reader, and select File>save as text from the menu. To convert lit format to html download a OS X program from this guy: http://hansr.net called lit2html, you can download lit2html here (right click & save as): http://www.hansr.net/Lit2html.dmg. You can drag & drop a lit file to the lit2html application, and it will create a folder containing all components inside the lit file. To convert this e-book to pdb, just drag the .htm file with the title of your book (generally the one with the largest filesize) to PorDiBle.
I’m not aware of any similar applications for windows. If you run windows you’ll have to look for conversion utilities. If you find any good ones, please reply to this post.

Now that you have your .pdb file you are ready to get them to you eReader application. Because the application storage can not be accessed externally you will have to download the e-books from inside the application. To do this you will need to browse to a website, containing links to your books with ereader as protocol. Example: ereader://http://www.somewebsite.com/book.pdb or on a local LAN server: ereader://192.168.1.3/books/book.pdb. You’ll need a web server to do this with your own books. I will not go into the particulars of setting one up on your local machine if you do not have one available. Many, many tutorials exist already for that purpose.
When you have a web server available to you, make sure it supports PHP scripts. I’ve created a PHP script that will automatically create a web page containing eReader links to all files in the directory where the scripts reside. The only thing you will have to do is create a directory on your web server (named ebooks for example) containing this script in a plain text file with the filename “index.php”. You can download the script here (right click, save as): http://weblog.oxidiser.nl/wp-content/uploads/2009/08/index.phps. Just rename the file from index.phps to index.php. You’re all set to download e-books into eReader after you’ve uploaded this file to your directory. The only thing left is drop in your pdb e-book files into this same directory. Just browse to this folder in safari on your iPhone or iPod touch (if you choose the directory e-books in the root of your web server it will be http://yourserver/ebooks). You will get a list with filenames of all the e-books currently in the ebooks folder. When you tap one, it will automatically start the eReader application and download the e-book into it.

That’s it! Now you have your e-book on your device ready to be read!

Using a PuTTY terminal as a SOCKS proxy through a remote host

May 29th, 2009 No comments

A useful thing I would like to share with you is how to set up PuTTY to use a remote server as a proxy server. This can for example be helpful to bypass a firewall or ban or you can use it to test services that are locked on one or multiple IP addresses. The added bonus off course is that SSH uses encryption up to the server you specify.

The first step is to download a suitable PuTTY binary for your platform at http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html.

The second step is filling in your servers hostname or IP address.
1Next up is specifying the  source port that you will use for the local connection. Go to Connection->SSH->Tunnels in the left pane. Fill in 8080 as the source port, and select dynamic as destination. Click the “Add” button.
2That’s it for the PuTTY part. If you want you can go back to the Session settings and give the connection a name, and save it for later use.
Now connect to the server by clicking “Open”. Fill in your servers username and password.

The last thing to do now is configure your proxy settings on your browser or other software. I’ll show you how to configure FireFox 3, but the settings are the same for every browser.
Go to Tools->Options->Advanced tab on your firefox browser. Click on the connection “Settings” button. Fill in the details like shown on the image below.
3Now you can browse to any website. You can check a website like http://www.watismijnip.nl to verify you have the same IP address as your server.

That’s it!

Categories: Computing Tags: , , , , , ,

Wolfram|alpha

May 26th, 2009 No comments

I found this cool new “search engine”. It is a bit different from google or other indexed search engines in that it indexes data instead of websites. This way you can ask wolfram a question and it will try to interpret it. And come up with relevant information and computes relevant graphs etc. So in short, make available data computable. Pretty cool. It can do all kinds of stuff like unit conversion, aerodynamics, looking up details about the human gnome, complex math, color calculations,  nutritional information, matching crossword puzzle letter sequences, showing name popularity, probability etc. etc.

Here are some cool examples:
Weather in The Hague, The Netherlands
10 km/h unit conversion
Gravitational constant 
Speed of light
Plumbum
Relative velocities
10 grams of water
International space station

For more cool stuff check out the examples and introduction screencast.

Or go to http://www.wolframalpha.com/ to try it out for yourself.

Optimizing filesystem performance for webservers in linux by disabling access time writes

March 31st, 2009 No comments

I just came across some interesting reading material concerning the linux POSIX compliance for file systems. I stumbled upon it after looking for the reason (or a solution) for one of my webservers which has about 95% write I/O instead of read I/O, which I thought was weird for that particular load. To be compliant with POSIX access times should be registered thus this is enabled by default.  So it could be noticeably faster for web servers to disable the writing of atime timestamps to each file that is read at each request (it will even write an atime-stamp to disc when the whole thing is cached!). This is off course dependant on your hardware, software and system load.

It should be noted however that some back-up software solutions (archiving) need the atime stamp to work properly. One of the other downsides of this is that you can no longer find old unused files with a command like:

find /var/www/html -atime +[time in days in the past] -print

Or offcourse, the use of atime timestamps in forensics.

To disable the access time timestamp writing add the option “noatime” to your filesystem mounts. Remember that your file system mounts and their options can be found in the /etc/fstab file of your linux install. After editing your partition you can remount all partitions with

mount -a

or only the changed mount with:

mount -o remount /
# With / being the name of your partition or device name.

I still have no idea how this would work out on the box in question. It might just be a large numer of logs written to disc (etcetera) instead of atime updates resulting from the many file reads a web server does. I guess I will just have to try it out to see if it makes a real difference for that box.

Pinging your sitemap to google after every edit or addition through a CMS with zend framework

January 28th, 2009 No comments

This helpful action helper can be called from all edit and add actions from your CMS controllers. It checks if enough time has elapsed after the last ping, using Zend_Cache. It logs ping errors to the log through the logger that is initialized in the initializer (ZF >1.6 project layout). You can replace it with any action you like. The actual generation of the XML sitemap is not in the scope of this post.

<?php
/**
 * This action helper pings google to index the changed urls through the sitemap.
 * 
 * @version 1.0
 * @uses Zend_Controller_Action_Helper_Abstract
 */
require_once 'Zend/Controller/Action/Helper/Abstract.php';
require_once 'Zend/Cache.php';
require_once 'Zend/Date.php';
require_once 'Zend/Http/Client.php';
class Zend_Controller_Action_Helper_GoogleSitemapPing extends Zend_Controller_Action_Helper_Abstract
{
    /**
     * Executes the ping to google.
     * 
     * @return boolean Was google pinged or not?
     */
    public function direct ()
    {
        // Only submit sitemap if this is the production instance of this application.
        $env = Zend_Controller_Front::getInstance()->getPlugin('Initializer')->getEnv();
        if($env == 'production') {
            // Get sitemap publish cache.
            $cacheFrontendOptions = array('automatic_serialization' => true);
            $cacheBackendOptions = array('cache_dir' => APPLICATION_PATH . '/../tmp/googleSitemapPing');
            $cache = Zend_Cache::factory('Core', 'File', $cacheFrontendOptions, $cacheBackendOptions);
            $shouldPing = false;
            if(!($data = $cache->load('lastPing'))) {
                // Cache miss. Should ping.
                $shouldPing = true;
            } else {
                // Cache hit, check if enough time has elapsed since last ping.
                // May only submit sitemap once per hour. Once an hour is a bit much, lets make it two hours.
                if(Zend_Date::getTimestamp()-$data > 7200) {
                    // More than a hour has passed. Do ping to google.
                    $shouldPing = true;
                }
            }
            if($shouldPing) {
                // Do http request to the google sitemap ping frontend.
                // Setup new http client.
                $client = new Zend_Http_Client();
                $client->setConfig(array('timeout' => 10, 'useragent' => 'ZF Google sitemap pinger V1.0', 'maxredirects' => 2, 'keepalive' => true));
                $client->setUri('http://www.google.com/webmasters/tools/ping?sitemap='.urlencode('http://www.yoursite.com/google-sitemap/index/gz/on'));
                $response = $client->request();
                if ($response->isSuccessful()) {
                    // Set new timestamp.
                    $cache->save(Zend_Date::getTimestamp());
                    return true;
                } else {
                    // Ping not successful, add notice to error log.
                    $logger = Zend_Controller_Front::getInstance()->getPlugin('Initializer')->getLogger();
                    $logger->log('Could not ping google sitemap ping service!', Zend_Log::ERR); 
                }
            }
        }
        return false;
    }
}