There’s been quite alot of talk lately about array dereferencing which I think will be included in php 5.4.
Here I’ll show you a couple of tricks I use for array and object dereferencing.
More details on function array dereferencing wiki.php.net
We can already chain methods by returning objects from methods.
Example
class Foo {
public function storeMessage($message) {
$this->message = $message;
return $this;
}
public function showMessage() {
reutrn $this->message;
}
}
$foo = new Foo();
echo $foo->storeMessage('hello world')->showMessage();
Chaining is great (but don’t get too carried away). Sometimes we don’t even need to keep the new object we create because we can do everything we want to do with it in a short space of code. Here it becomes handy to be able to redreference the object at construct.
echo (new Foo())->storeMessage('hello world')->showMessage();
Theres nothing wrong with that apart from you need php 5.4 to do it.
Using the with function work around.
function with($object) {
return $object;
}
echo with(new Foo())->storeMessage('hello world')->showMessage();
Very short function which just returns the object it was given.
Array dereferencing is being able to access an element in the array when it’s returned from a method or function.
Example
function giveMeAnArray() {
return array('color' => 'red', 'size' => 12);
}
echo giveMeAnArray()['color'];
This would produce an error until php 5.4.
The work around I use. Example
function giveMeAnArray() {
return array('color' => 'red', 'size' => 12);
}
echo array_get(giveMeAnArray(), 'color');
Now array_get doesn’t exist in php core. But is a function I find quite handy.
Some of the functions I use daily don’t exist in the PHP core. But the great thing about programming languages is you can create your own functions.
Here is handy function I’ve been using alot of lately.
Originally created by Andrew Shearer
if (!function_exists('array_get')) {
function array_get($arr, $key, $default = false) {
if (array_key_exists($key, $arr)) {
return $arr[$key];
}
else {
return $default;
}
}
}
You might use it in a constructors config array.
class Foo {
public function __construct(array $config = array()) {
$this->bar = array_get($config, 'foo', 'my default value');
}
public function showValue() {
return $this->bar;
}
}
$foo = new Foo();
echo $foo->showValue(); // outputs "my default value"
$foo = new Foo(array( 'foo' => 'new value' ));
echo $foo->showValue(); // outputs "new value"
It can greatly simplfy code.
As an alterative you could use an config default merge.
class Foo {
public function __construct(array $config = array()) {
$config = $config + array(
'foo' => 'my default value'
);
$this->bar = $options['foo'];
}
public function showValue() {
return $this->bar;
}
}
$foo = new Foo();
echo $foo->showValue(); // outputs "my default value"
$foo = new Foo(array( 'foo' => 'new value' ));
echo $foo->showValue(); // outputs "new value"
I’ve been using Flot a Javascript plotting library for jQuery in a few projects recently. I came across the need to know when all the charts have been drawn.
Thankfully Flot has nifty support for plugins. So I wrote one. It’s quick and simple.
(function ($) {
// charts will store how many charts initialised, drawn will store how many have been drawn.
var charts = 0, drawn = 0;
function monitor() {
// if we have more charts than there are charts drawn, check again in a little while
if (charts > drawn) {
setTimeout(monitor, 50);
} else {
// all charts have finished being drawn, if theres a function call it
if (window.flotCompleted instanceof 'function') {
window.flotCompleted();
}
}
}
// init is called for every flot chart
function init(plot) {
// we have another chart
charts++;
plot.hooks.draw.push(function() {
// a chart has finished being drawn
drawn++;
});
}
// register the plugin
$.plot.plugins.push({
init: init,
name: 'loadingmonitor'
});
// give the browser time to have all the charts registered
setTimeout(monitor, 200);
})(jQuery);
Sometimes we need to know how much disk-space a table is using. Or which table is using the most disk-space. Here’s a handy query.
Here’s a handy query which uses the information_schema tables MySQL provides.
It can take MySQL sometime to build the information up required to produce the results.
SELECT CONCAT(table_schema, '.', table_name) AS table_name, CONCAT(ROUND(data_length / (1024 *1024), 2) , 'M') AS data_length, CONCAT(ROUND(index_length / (1024 *1024), 2) , 'M') AS index_length, CONCAT(ROUND((data_length + index_length ) / (1024 *1024), 2), 'M') AS total_size, CURRENT_DATE AS last_update FROM information_schema.tables WHERE table_schema = 'database_name' ORDER BY ROUND((data_length + index_length) / (1024 *1024), 2) DESC;
Note; Deleting tables and getting a smaller size from the query doesn’t necessarily mean that the disk space will be freed. InnoDB keeps the disk space used and marks as available in it’s table-space.
After restarting MySQL you’re left with a cold machine. No cache. Putting this directly back into use can leave it struggling to load data from disk again.
One way of helping out is to give it some time to load table data back into that vast amount of RAM it has.
One way I’ve found that’s helped me is to create a temporary table, alter it’s engine into a black hole then select everything from an existing table into it.
CREATE TEMPORARY TABLE blackholeMyTable ENGINE=BLACKHOLE SELECT * FROM MyTable;
This cause MySQL to go though the works of loading the data from disk to insert into a blackhole. Thus being put into RAM.
Sometimes there’s a need to tail log files. This might be to store data in a more structured form, say in a database. Maybe you need to tail al log file to monitor for a certain condition. These log files could be anything, like maybe Apache or Mail logs.
PHP doesn’t come with any easy way built in to tail a file. Unix comes with the handle tail tool. I use tail regularly mixed with a grep, looking for a certain entry which I know will appear.
// tail the apache log only showing line containing the local IP. $ tail -f /var/log/apache.log | grep 127.0.01
// create tail object with a file containing our log data
$tail = new FileTail('/var/log/apache.log');
// if we have a previous position we ran up to restore that position
if (is_file('pos')) {
$tail->setPos(file_get_contents('pos'));
}
// while we can read aline
while ($line = $tail->readLine()) {
// store where we've got to
file_put_contents('pos', $tail->getPos());
// use a regular expression to read values out of the line
....
// use a database connection to store
....
}
This code would run until stopped. With blocking turned on, TailFile will wait and keep checking for newly appended lines.
Facebook’s “phpsh” - interactive shell for PHP -
One of the best PHP shells I’ve seen. Made by the development team at Facebook.
Unfortunately mostly written in python, but thats available on most systems.
A recent post from php.net explains a tool I didn’t know existed pman. Unix man pages of PHP functions.
This little tool makes accessing manual pages for PHP functions incredibility fast.
Installation is very easy:
# using pear to install the tool
$ pear install doc.php.net/pman
# use the command pman followed by the function name
$ pman strlen
This example instantly displays a unix style man page loaded locally of the strlen docs.
This is a great way of keeping a local copy of PHP docs for those times when you can’t access php.net.
I also find the unix style man docs easier to read. After all you only need to get used to reading one style of manual page (although php.net manual style has been some of the best I’ve come across).
Recently I stumbled into an issue with the PHP FTP functions. After successfully connecting to a server and authenticating, fetching a file list using ftp_nlist would return NULL. This isn’t even a return type listed in the manual.
$ftp_server = 'ftp.example.com';
// set up basic connection
$conn_id = ftp_connect($ftp_server) or die("Couldn't connect to $ftp_server\n");
$ftp_user_name = 'user';
$ftp_user_pass = 'pass';
// login with username and password
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass) or die("Login failed");
// get contents of the current directory
$contents = ftp_nlist($conn_id, ".");
// output $contents
var_dump($contents);
Why was it failing? It was connecting fine, there where no problems with authenticating.
As a developer getting into this situation is very frustrating. Where do we turn to?
One solution is strace (presumably short for “system trace”), a system debugging tool for linux. There are other Unix variants like dtruss and dtrace. In this article we’ll concentrate on strace.
The tool traces system calls and signals made by the process you tell it to trace. The name of each system call, its arguments and its return value are printed on standard error, which isn’t pretty. I find it helpful to output the results to a file (-o filename argument to strace can help), alternatively you can redirect standard error to standard out and pipe the results though grep.
There are two ways you can tell it to trace a process, which depends on if the process is already running or not.
Attach to the process with the process ID pid and begin tracing. The trace can be terminated at any time by a keyboard interrupt signal (ctrl-c). strace will detach itself from the process leaving it to continue running. This can be handy to look at what a long running script is currently doing. This includes things like Apache.
// -p pid $ strace -p 123
The other way we can trace something is by telling it the command we want executed and traced. Anything which is not one of its own arguments is the command it will execute, anything after that are arguments to the executing command.
// -e is an argument of strace with the value of open // -v is an argument of php with no value $ strace -e open php -v 2>&1 | grep php.ini // outputs the location of the php.ini file which was loaded
Running the command:
strace -e open php -v 2>&1 | grep php.ini
Outputs:
open("/usr/bin/php.ini", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/php5/cli/php.ini", O_RDONLY|O_LARGEFILE) = 3
Here we see that PHP attempted to open two files. One in directory where the binary is, and one in /etc/php5/cli/ this is where PHP has been compiled to look. The first system call failed with No such file or directory.
You can learn more about system calls and their return values from the linux documentation. http://linux-documentation.com/en/man/man2/open.html
Example Command:
// -e open, trace only the open system call // -v ask php to show it's version // 2>&1 redirect stderr to stdout // | grep php.ini filter the output for the string php.ini $ strace -e open php -v 2>&1 | grep php.ini // outputs the location of the php.ini file which was loaded :)
Example Output:
open("/usr/bin/php.ini", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/php5/cli/php.ini", O_RDONLY|O_LARGEFILE) = 3
php.ini is being loaded from /etc/php5/cli/php.ini
Example Command:
strace -e open php test.php 2>&1 | grep include.php
Example Output:
open("/usr/bin/include.php", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
Oops maybe we have the wrong path to include the file.
$ strace -s 300 -e connect,send,recv php ftpTest.php
The output from this strace showed that ftp_nlist was asking the remote server to connect in on a local IP. Clearly an internet ftp server can’t connect in to our local ip. PHP’s error handling on this is clearly insufficient, but thanks to the handy functionality of strace was easy to find. In this case the fix was to make the connection pasv.