Tag Archives: PHP

Simple PHP proxy for cross origin HEAD requests

Have some Javascript that runs through all the URLs on a site of mine to prep caching and check for dead links and other problems. Problem is that the site also includes embedded video files hosted by someone else. These fails of course because of cross origin restrictions and since I’m not in control of their servers I needed a workaround.

I wrote this small PHP script to work as a proxy and as far as I can see it works pretty great. Short and easy to follow as well, so that’s always fun 🙂

So basically the script just pipes through whatever headers it gets to the target URL and pipes back whatever headers it gets in the response. Seems to work, but let me know of weaknesses and easy improvements if you see any 🙂

<?php

$request_headers = getallheaders();
unset($request_headers['Cookie']);
unset($request_headers['Host']);

foreach($request_headers as $key => &$value)
    $value = $key.': '.$value;

$url = $_SERVER['QUERY_STRING'];
$limit = 20;

$curl = curl_init();
do
{
    curl_setopt_array($curl, array
    (
        CURLOPT_URL => $url,
        CURLOPT_HTTPHEADER => $request_headers,
       
        CURLOPT_RETURNTRANSFER => TRUE,
        CURLOPT_HEADER => TRUE,
        CURLOPT_NOBODY => TRUE,

        CURLOPT_FOLLOWLOCATION => TRUE,
        CURLOPT_MAXREDIRS => $limit--,
    ));

    ob_start();
    $headers = trim(curl_exec($curl));
    $url = curl_getinfo($curl, CURLINFO_REDIRECT_URL);
    ob_end_clean();
}
while($url and $limit > 0);

curl_close($curl);

$headers = trim(substr($headers, strrpos($headers, "\r\n\r\n")));
header_remove();
foreach(explode("\r\n", $headers) as $h)
    header($h);

Notes

  • The Cookie and Host headers sent from the browser are removed so they don’t mess up the request.
  • CURL wasn’t acting properly where I deployed this. It didn’t follow redirects, and curl_exec output content to the browser even with return transfer set to true. So it has a manual workaround for following the redirects and uses output buffering to make sure nothing goes to the browser before we want to.

PHP: Simple directory recursion

Keep running into scenarios where I need to scan through a file system and it’s actually pretty simple if you just know what classes to use. So… note to self and others:

// This should return false if there is something you want excluded
function filter($file, $key, $iterator)
{
    $exclude = array('.git');
    return ! in_array($file->getFilename(), $exclude);
}

// Recursive directory iterator for current directory, ignoring dots
$it = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
// Wrapped by a filtering iterator with our filter function
$it = new RecursiveCallbackFilterIterator($it, 'filter');
// Wrapped by an iterator which automatically traverses children for us
$it = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::SELF_FIRST);

// And then just loop :)
foreach($it as $file)
{
    echo str_repeat("\t", $it->getDepth())
        . $file->getRealPath()
        . PHP_EOL;
}

This skips the annoying dots, properly excludes directories you don’t want and pretty much works the way it should.

Order WordPress RSS feed by modified rather than published

Adding the following to the functions.php of your theme should make the posts in your WordPress RSS feed sort by when the post was last modified rather than when it was published. Useful in some cases when you for example want to push out updated posts as well.

function wpse49312_alter_the_query( $request )
{
    $dummy_query = new WP_Query();
    $dummy_query->parse_query($request);

    if($dummy_query->is_feed())
        $request['orderby'] = 'modified';

    return $request;
}
add_filter('request', 'wpse49312_alter_the_query');

PHP: How to use IntlDateFormatter for local month names

Here’s how to get PHP to output a date with a full local month name.

setlocale(LC_ALL, 'no_NB.utf-8', 'nor'); // For other stuff
Locale::setDefault('no_NB.utf-8'); // For IntlDateFormatter

$f = new IntlDateFormatter(null, null, null, null, null, 'd. MMMM y');
echo $f->format(new DateTime("2013-01-01"));

// Outputs: 1. januar 2013

In this case I just use a hardcoded norwegian long date format, but check out the IntlDateFormatter constructor and you will find various date and time constants for various formats.

Note: Requires PHP version >= 5.3 and the intl.so extension active. If on Dreamhost you can do this by adding the following to your ~/.php/5.3/phprc

extension = intl.so

PHP: Catch all exceptions and errors in a normalized and consistent matter

For my small website myhymnal.net I wanted a nice error handler that caught all errors and presented them as nice error pages. The problem is that you have at least three kinds of errors which you have to handle in PHP. Uncaught exceptions, regular errors and fatal errors. After some experimenting I think I’ve found a way to catch them all in a close to consistent matter so that they are easy to deal with.

error_reporting(ENV === 'dev' ? E_ALL : 0);

set_error_handler("error_handler");
set_exception_handler("error_handler");
register_shutdown_function("error_handler");

function error_handler()
{
    // Check for unhandled errors (fatal shutdown)
    $e = error_get_last();

    // If none, check function args (error handler)
    if($e === null)
        $e = func_get_args();

    // Return if no error
    if(empty($e))
        return;

    // "Normalize" exceptions (exception handler)
    if($e[0] instanceof Exception)
    {
        call_user_func_array(__FUNCTION__, array(
            $e[0]->getCode(),
            $e[0]->getMessage(),
            $e[0]->getFile(),
            $e[0]->getLine(),
            $e[0]));
        return;
    }

    $e = array_combine(array('number', 'message', 'file', 'line', 'context'), array_pad($e, 5, null));
   
    var_dump($e);
    exit;
}

This way the only irregularity should be the last property, context. In cases of errors this will be an array with variables in the context where the error occurred, while for exceptions I’m setting it to the Exception itself. Normally you’d then pass $e to something that would format the information nicely to the user, do some logging or whatever.

How to install PHPUnit on Windows

Note to self in case I need to do this again…

Commands assumes the PHP install is in C:\wamp\bin\php\php5.3.13 and that this path is already added to the PATH environment variable.

Install PEAR

  1. Download go-pear.phar and Save it as C:\wamp\bin\php\php5.3.13\pear\go-pear.phar.
  2. Open an elevated command prompt.
  3. Go to the PHP directory.
    > cd \wamp\bin\php\php5.3.13
  4. Install PEAR, pressing enter to accept defaults and Y to alter php.ini.
    > php .\pear\go-pear.phar
  5. Add environment variables.
    > start PEAR_ENV.reg

Install PhpUnit

  1. Make sure pear is fully up to date (should be if we just installed it)
    > pear upgrade-all
  2. Add pear channels.
    > pear channel-discover components.ez.no
    > pear channel-discover pear.phpunit.de
    > pear channel-discover pear.symfony.com
  3. Install PHPUnit with all dependencies.
    > pear install --alldeps phpunit/PHPUnit
  4. Verify installation by checking version.
    > phpunit --version