Subscribing to iTunes Podcasts without iTunes

Update: I’ve released an open source version of this script in php. Be sure to try out the fully functioning demo.


I often find myself discovering podcasts that are only provided as an iTunes link. Content providers risk excluding a significant number of users by not providing a standard link to the original podcast feed.

The following bash script (itx.sh) will extract the podcast’s feed url so that you may subscribe using an rss client of your choice:

#!/bin/sh

# extract the podcast id from the first argument
id=$(echo $1 | sed -r 's/^(http.+\/id([0-9]+)|([0-9]+)$).*/\2\3/')

# the podcast id was not supplied if $id isn't an integer
c=$(echo $id | tr -d 0-9)
if [ $# == 0 ] || [ -n "${c}" ]; then
  echo The podcast ID must be in one of the following formats:
  echo "- itx https://itunes.apple.com/gb/podcast/above-beyond-group-therapy/id286889904"
  echo "- itx 286889904"
  return
fi

# curl a request to iTunes and extract the feedURL attribute
url="https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/com.apple.jingle.app.finance.DirectAction/subscribePodcast?id=${id}&wasWarnedAboutPodcasts=true"
feedUrl=$(curl -s -A "iTunes/9.1.1" "${url}" | grep "feedURL" | sed -r "s/.+<string>([^<]+).+/\1/")

# return the source podcast url if it was found
if [ -n "${feedUrl}" ]; then
  echo $feedUrl
fi

Reference: get the latest podcasts from itunes store with link by RSS, JSON or something

Find and Replace Text Recursively with Bash

Linux/Unix based platforms have a variety of CLI tools which are incredibly useful. I had a scenario where I needed to update a path reference in various configuration files, fortunately this can be accomplished using a single command:

grep -rl 'foo' . | xargs -l1 sed -i 's/foo/bar/g'

Note: Forward slashes must be escaped from your find and replace strings passed to sed, e.g.

cd /etc/apache2
grep -rl '/etc/apache' conf | xargs sed -i 's/\/etc\/apache/\/etc\/apache2/g'

The above command omits the -l1 argument to xargs. This executes slightly faster as all matching paths are sent to sed at once rather than constructing a foreach loop. If you expect a lot of files to match your search string you’ll want to pass the -l1 argument to avoid a too many arguments error from sed.

Custom Form Validation Error Messages in CodeIgniter 2

<?php

class MY_Form_validation extends CI_Form_validation
{
    private $_custom_field_errors = array();

    public function _execute($row, $rules, $postdata = NULL, $cycles = 0)
    {
        // Execute the parent method from CI_Form_validation.
        parent::_execute($row, $rules, $postdata, $cycles);

        // Override any error messages for the current field.
        if (isset($this->_error_array[$row['field']])
            && isset($this->_custom_field_errors[$row['field']]))
        {
            $message = str_replace(
                '%s',
                !empty($row['label']) ? $row['label'] : $row['field'],
                $this->_custom_field_errors[$row['field']]);

            $this->_error_array[$row['field']] = $message;
            $this->_field_data[$row['field']]['error'] = $message;
        }
    }

    public function set_rules($field, $label = '', $rules = '', $message = '')
    {
        $rules = parent::set_rules($field, $label, $rules);

        if (!empty($message))
        {
            $this->_custom_field_errors[$field] = $message;
        }

        return $rules;
    }
}

?>

Include CodeIgniter Models using spl_autoload

CodeIgniter’s models may be added to autoload.php (which will always load them even if they’re not required) or must be manually loaded using code such as:

$this->load->model('views\account\logon');

While it’s only one line of code, it soon becomes tedious having to have a similar line across every action in every controller. Consequently, I created a CodeIgniter library (after taking inspiration from Doctrine’s ClassLoader) which uses spl_autoload. Class_loader automates the process of loading classes within a defined namespace and only requires a single line of configuration; therefore no changes to autoload.php or manual calls to load are required.

Class_loader.php:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Class_loader is a CodeIgniter library which uses SPL autoload to
 * automatically include classes within the defined namespace.
 * 
 * @author Andrew Mackrodt <andrew@ajmm.org>
 * @version 2011.06.23
 */
class Class_loader
{
    /**
     * @param string $namespace The namespace to apply the autoloader.
     * @param string $includePath
     *     The parent folder of the root namespace. Defaults to CodeIgniter's
     *     APPPATH if no value is given.
     */
    public function register($namespace, $includePath = null)
    {
        if (!empty($namespace) && $namespace[0] == '\\')
        {
            // Strip leading namespace separator.
            $namespace = substr($namespace, 1);
        }

        if (empty($includePath))
        {
            // Set the include path to the CI application folder.
            $includePath = APPPATH;
        }

        // Ensure the include path ends with the OS directory separator.
        $includePath = rtrim($includePath, '\\/').DIRECTORY_SEPARATOR;

        // Register the auto-loader.
        $classLoader = new _ClassLoader($namespace, $includePath);
        spl_autoload_register(array($classLoader, 'loadClass'));
    }
}

/**
 * A helper class to be used by Class_loader. 
 */
class _ClassLoader
{
    private $namespace;
    private $includePath;

    public function __construct($namespace = null, $includePath = null)
    {
        $this->namespace = $namespace;
        $this->includePath = $includePath;
    }

    public function loadClass($className)
    {
        if ($this->namespace !== null
                && strpos($className, $this->namespace.'\\') !== 0)
        {
            return false;
        }
        
        // Convert a namespace to a path using the OS directory separator.
        $classPath = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $className);
        
        require_once $this->includePath.$className.'.php';
        return true;
    }
}

Usage:

The classes you wish to autoload must contain a namespace and be located appropriately, e.g. I have a Script class which is located at APPPATH/models/views/shared/Script.php and has the namespace models\views\shared.

$this->class_loader->register('models\views', APPPATH);

Deny Access to a Directory with IIS

Apache is the dominant server for PHP content although IIS can be a respectable substitute. However, upon first inspection IIS does not appear to be as flexible as Apache, particularly regarding dynamic configuration through .htaccess. There are generally three main areas where .htaccess files are used:

  • URL rewriting
  • Providing basic or digest authentication
  • Denying access to a directory

The CodeIgniter framework for example contains an .htaccess file in the root of the application and system folders to deny direct access to important site resources. Additionally, we may wish to have an uploads folder where file access is managed through our PHP application; we cannot use ACLs in such an example as the anonymous IIS user must have access to the resource yet the client should not be able to access it directly.

Apache lets us define this behavior easily by adding one line to an .htaccess file in the protected directory:

deny from all

The client will now receive a 403 Forbidden error should they try to to access the directory or any resource within. ASP.NET developers may use forms or windows authentication to address the problem but these are not easily available from a PHP application. Luckily IIS 7 includes a Request Filtering module which allows us to deny access to a directory using a web.config file:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <security>
            <requestFiltering>
                <denyUrlSequences>
                    <add sequence="/" />
                </denyUrlSequences>
            </requestFiltering>
        </security>
    </system.webServer>
</configuration>

This configuration will send the client a 404.5 Not Found status. While the status code is not as accurate as Apache’s 403 status, it does at least prevent access to the directory and it’s resources.

Continue reading

IIS Directory Permissions and File Uploads

IIS 7 requires the following user accounts to have read access to your webroot: IIS_IUSRS and IUSR. For a typical ASP.NET application IIS_IUSRS may also be granted write permissions for specific directories or files. However, if impersonation is used (e.g. fastcgi.impersonate is recommended for PHP), then the IUSR account must also be granted write permissions to the resources.

The following command-line actions will set the permissions correctly (note: replace C:\inetpub\wwroot with your webroot):

ICACLS "C:\inetpub\wwwroot" /Grant "IIS_IUSRS":(OI)(CI)M
ICACLS "C:\inetpub\wwwroot" /Grant "IUSR":(OI)(CI)M

Using PHP Reflection to read a Protected Property

Sometimes it’s necessary to obtain the value of a protected or private property. Fortunately PHP 5.3 includes a new reflection method named setAccessible which allows us to do so.

The Method:

/**
 * Get the value of a property using reflection.
 *
 * @param object|string $class
 *     The object or classname to reflect. An object must be provided
 *     if accessing a non-static property.
 * @param string $propertyName The property to reflect.
 * @return mixed The value of the reflected property.
 */
public static function getReflectedPropertyValue($class, $propertyName)
{
    $reflectedClass = new ReflectionClass($class);
    $property = $reflectedClass->getProperty($propertyName);
    $property->setAccessible(true);

    return $property->getValue($class);
}

Example Usages:

getReflectedPropertyValue($foo, 'myProtectedProperty');
getReflectedPropertyValue('Foo', 'myProtectedStaticProperty');
getReflectedPropertyValue('Foo\Bar', 'myProtectedStaticProperty');

SQL Error (1064) when Creating a Trigger in MySQL

“SQL Error (1064): You have an error in your SQL syntax” occurs when a query has been incorrectly delimited. This is a common cause of confusion when creating triggers as the internal statement needs to be terminated in addition to the create or alter trigger statement. MySQL uses a semi-colon as the default delimiter and does not work with nested delimiters, e.g.

CREATE TRIGGER ... AFTER INSERT ON ...
FOR EACH ROW
BEGIN
    INSERT ...;
END;

Notice how there are two semi-colon delimiters in the above example. We need to temporarily define a different delimiter to workaround this problem, the following example uses a double forward slash:

DELIMITER //;

Now we need to add the new delimiter at the end of our statement so that it looks like this:

CREATE TRIGGER ... AFTER INSERT ON ...
FOR EACH ROW
BEGIN
    INSERT ...;
END;//

Assuming that there are no other syntax errors, the statement should execute without any errors. To restore the delimiter to a semi-colon use the following query:

DELIMITER ;//

Included below is a full example with the necessary table schema:
Continue reading

Doctrine 2 CustomStringFunction and Undefined Index

Doctrine 2 allows for user defined functions to be created, this allows us to add vendor specific functions such as MySQL’s CONCAT_WS. I had the need to implement my own functions in a project I’ve been working on as DQL does not support a way to CONCAT expressions that contain null values without returning null (i.e. there is no IFNULL or ISNULL function).

After reading the Adding your own functions to the DQL language documentation and attempting to implement my own function I was repeatedly getting the following error: Undefined index and Class name must be a valid object or a string. I had a feeling that this was to do with my custom function not being auto-loaded although I was certain that I had followed the documentation correctly.

Having looked through Doctrine’s ORM classes I discovered a function named CustomFunctionsReturningStrings inside Parser.php. I set a breakpoint inside the function and was surprised to see that it never gets called. This is a bug in versions of Doctrine prior to 2.0.5.

If you are unable or do not wish to upgrade between versions but still need this fix you can apply the following patch to \Doctrine\ORM\Query\Parser.php:

2304c2304,2305
<                 return $this->FunctionsReturningStrings();
---
>                 // do NOT directly go to FunctionsReturningString() because it doesnt check for custom functions.
>                 return $this->FunctionDeclaration();