The Baby-Luck Blog

To content | To menu | To search

Sunday 2 February 2014

How many levels before we call 'Enterprise' frameworks what they are?

A little while ago we had some code in use where these were the levels the code runs thru to get from the app.php which Apache rewrite directs all requests to, to the function that will test the password against the loaded password from the db (including hashing, etc.)

I count 16. six...teen. Frankly it would be difficult to convince me this, in the end, is much more than impenetrable junk. All made possible by Symfony 2.


/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Encoder/MessageDigestPasswordEncoder.php:64 Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder->isPasswordValid
/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/DaoAuthenticationProvider.php:66 Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider->checkAuthentication
/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/UserAuthenticationProvider.php:85 Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider->authenticate
/app/cache/prod/classes.php:2484 Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager->authenticate
/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php:88 Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener->attemptAuthentication
/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AbstractAuthenticationListener.php:144 Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener->handle
/app/cache/prod/classes.php:2361 Symfony\Component\Security\Http\Firewall->onKernelRequest
/app/cache/prod/classes.php:0 call_user_func
/app/cache/prod/classes.php:1676 Symfony\Component\EventDispatcher\EventDispatcher->doDispatch
/app/cache/prod/classes.php:1609 Symfony\Component\EventDispatcher\EventDispatcher->dispatch
/app/cache/prod/classes.php:1773 Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch
/app/bootstrap.php.cache:2794 Symfony\Component\HttpKernel\HttpKernel->handleRaw
/app/bootstrap.php.cache:2779 Symfony\Component\HttpKernel\HttpKernel->handle
/app/bootstrap.php.cache:2908 Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle
/app/bootstrap.php.cache:2210 Symfony\Component\HttpKernel\Kernel->handle
/web/app.php:23 {main}

Saturday 19 October 2013

Named function arguments in BASH

BASH is quite a different language from my usual. And starting with it feels like going back in time somehow, when everyone knew some Assembly or something. To make it a little easier to handle I've written some helper functions... just like everyone else I suspect.

Anyhoo, here's one I use regularly for function arguments.

A pretty common way of calling a function with arguments and using them in the function might look like:

function some_fn () {
local msg=$1
local val=$2
echo "msg [$msg] val [$val]"
some_fn haha 6
but instead you can do this:
function new_fn () {
echo "msg [$msg] val [$val]"
new_fn msg haha val 6

The script treats all arguments to the function as name-value pairs introduces them as variables into scope using local.
There is a setting variable in the script that can be changed to introduce a prefix to the variable names if you want to clearly mark the variable as an argument (perhaps "A_" which will result in the variables in the example being named A_msg and A_val).

You can download the script here

This is much more like what I'm used to in JS and PHP where you can name your arguments, the merits of which seem great.

Saturday 28 September 2013

Amarok Moodbars script

This is from a little while ago but it's always nice to make your code available. Here is a script for the Amarok music player, retrievable as the Moodbars-BL script from KDE-apps

Moodbars are ususally small graphics shown as a background to the elapsed time bar commonly used to show the progress of a track of music. The graphic is a representation of the track produced by some mathematical analysis. This analysis can be complex but in the case of the default in Amarok is more simple it seems. However you can tell the busy-ness and quiet spots of the track easily which is quite nice.

Anyway, the script automatically generates moodbar files for any local songs in the Amarok playlist. It is written in JS and is configurable in the "Scripts" section of the Amarok configuration. There is more information on the KDE-apps site, with instructions inside the download.

Sunday 25 August 2013

Catching and handling PHP errors

If you get errors on your site, if you can help it you don't want to have to be reviewing log files or waiting for users to tell you. You want to catch these errors and be notified. You can try set_error_handler('our_error_handler'); but that will only be called for runtime errors such as E_ERROR. The other serious errors E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR occur in the target script before execution and are serious enough to prevent the script even entering runtime.

So you can see a lot of code try to use set_error_handler() or register_shutdown_function() (which we will actually use for our solution) to catch all the errors but they often place the assignments themselves in runtime code so critically they won't work.

But the assignment can be made before runtime in a file set as the auto_prepend_file PHP setting. You can set the "auto_prepend_file" in a few ways:
  • Either in the main php.ini with auto_prepend_file = our_error_.php
  • Or in php.ini files in each and every dir you want it to apply to because the setting is PHP_INI_PERDIR 'changeable' (which means that the setting does not cascade down the subdirectories, you need to set in each one)
  • Or in an .htaccess file with php_value auto_prepend_file our_error_.php, where it does recurse so put in the document root if you want it to apply to all pages (but you need AllowOverride Options to be set in Apache's httpd.conf (or equivalent) so the setting is allowed).
Note that "The file is included as if it was called with the require() function, so include_path is used."

In this auto_prepend_file you can place the assignment for the register_shutdown_function() which will then be called every time a page ends, either normally, or due to *any* error. register_shutdown_function('our_error_finder');

Remember that this will execute before PHP enters runtime so even if there is a critical parse error of your page where normally nothing at all happens, this will still have executed and you will find yourself in our_error_finder() where we can ask PHP if there was an error and respond.

Inside the error handler you don't know what the state is so you can't rely on anything having been loaded or anything. Which means that you should only use what you have in the prepend file. This is good anyway, the more code you try to use the more chance your handler has of failing, then you're really stuffed. This includes broken references to images in any output you show to the user. (Note that in some installations the cwd changes during shutdown, for example to "/". So including a relative file will probably fail, so use absolute paths).

So, along with a few extras:
  • To discover if the error happened before runtime so a full HTML page can be printed
  • An attempt at email
  • Writes some context of the error to the log file
The following is code for the file to prepend:
// our_error_.php
function our_error_finder () {
if (null !== $e = error_get_last())
return our_error_response($e);
return null;
responds to an error.
if this happened before runtime this shows a full page (in runtime there may have been page content already).
$err is an array with keys type, message, file, line.
function our_error_response ($err) {
//if the error is not handled here, return false so that if this fn is called as a part of an error handler it will indicate the default handler should continue.
if (!in_array($err['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR)))
return false;
$email_addrs = bl_error_email_addrs();
$log_fp = fopen(ini_get("error_log"), "a");
$message = "server-name [".$_SERVER['SERVER_NAME']."] when [".gmdate('Y-m-d H:i:s')."] ip-guess [".$_SERVER['REMOTE_ADDR']."] script-name [".$_SERVER['SCRIPT_NAME']."] query-string [".$_SERVER['QUERY_STRING']."] info [".json_encode($err)."]";
//by default php does not have stack_trace for fatal errors. the xdebug extension will provide a stack trace.
//but also provide the context to the log file
$wrote_log_bytes = 0;
if ($log_fp)
$wrote_log_bytes = fwrite($log_fp, "ERROR HANDLER context: ".$message.". will ".($email_addrs ? "send email to [".implode(", ", $email_addrs)."]" : "not send email").".\n");
if (in_array($err['type'], array(E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR)))
if ($_SERVER["SCRIPT_FILENAME"] == $err["file"])
$startup_error_in_main_page = true;
$emailsuccessp = null;
if ($email_addrs) {
$subject = 'WEBSITE ERROR for '.$_SERVER['SERVER_NAME'];
$body = $wrote_log_bytes ?
"the error should have been written to the log file (FYI: at ".gmdate('Y-m-d H:i:s')." ".$err["message"]." in ".basename($err["file"])."(".$err["line"]."))" :
$headers = 'Date:'.date('r')."\n".'From:the-system <>'."\n";
for ($i = 0, $ii = count($email_addrs); $i < $ii; $i++) {
if ($email_addrs[$i]) {
//try all emails, but keep record if any failed.
if (mail($email_addrs[$i], $subject, $body, $headers)) {
if ($emailsuccessp==null)
$emailsuccessp = true;
} else {
$emailsuccessp = false;
if ($log_fp) {
if ($email_addrs && !$emailsuccessp)
fwrite($log_fp, "error email(s) not sent :(\n");
if ($startup_error_in_main_page)
print '<html><head><title>Bad Bug</title></head><body>';
print '<div>Ouch!<br /><br />There was a rather significant error on the site, could be that nothing at all was done.<br /><br />';
if ($email_addrs) {
if ($emailsuccessp)
print 'I sent an email to the site administrators who should address the issue soon.<br /><br />You can of course chase them.';
print ' You can contact an administrator at <a href="mailto:'.$email_addrs[0].'">'.$email_addrs[0].'</a>.';
} else {
print 'I was not asked to contact anyone directly, but if you want to notify the administrator please tell them the time of the error: <code style="color:#B20;">'.date('Y-m-d H:i:s')."</code><br />";
print '<br /><br />KTHXBYE</div>';
if ($startup_error_in_main_page)
print '</body></html>';
return null;

You can download our_error_.php here.

I'll soon post a few more lines where you can try to recover from some simple function errors