ディレクトリー単位でphpファイルをまとめてrequire_onceする。

WordPress のテーマを開発している時にfunctions.phpをモリモリにするとワケワカメになってしまうので処理ごとに分割してrequire_onceします。
が、分けたファイルについてひとつ一つrequire_onceするのが億劫なので下記のclassをつくってディレクトリー単位で一括してrequire_onceするようにしてみました。

開発が一段落したらちゃんと構造的にインクルードした方がいいんでしょうね...

（2015/9/3 大幅に更新）
しばらく放置していましたが多少知識もつき、コードを一新しました。
コーディング規約はWordPressに準じていますがWordPressの関数は使用していません。※要（たぶん） >=PHP5.4

```php:fileloader.php
<?php
class FileLoader {

	/**
	 * @var Iterator
	 */
	private $iterator;

	/**
	 * Options
	 *
	 * @var array
	 */
	private $exclude_files = [];
	private $exclude_file_patterns = [];
	private $exclude_dirs = [];

	/**
	 * @access public
	 */
	public static function init( $dir, $recursive = false, $options = [] ) {
		if ( ! $dir || ! ( $dir = realpath( $dir ) ) || ! is_dir( $dir ) )
			return;
		new self( $dir, (bool) $recursive, (array) $options );
	}

	/**
	 * @access private
	 *
	 * @param  readable $dir
	 * @param  boolean  $recursive
	 * @param  array    $args
	 * @return void
	 */
	private function __construct( $dir, $recursive, Array $args ) {
		$this->prepare( $args );
		$this->setIterator( $dir, $recursive );
		$this->includeFiles();
	}

	/**
	 * @access private
	 *
	 * @todo
	 *
	 * @param  array $options {
	 *     @type string|array $exclude_files
	 *     @type string|array $exclude_dirs
	 * }
	 * @return void
	 */
	private function prepare( Array $options ) {
		$this->exclude_files[] = __FILE__;
		if ( ! $options )
			return;
		$options = filter_var_array( $options, self::getOptDef(), true );
		extract( $options );
		/**
		 * Exclude files
		 */
		if ( $exclude_files = array_filter( (array) $exclude_files ) )
			$this->exclude_files = array_merge( $this->exclude_files, $exclude_files );
		/**
		 * Exclude File Patterns (Regexp)
		 */
		if ( $exclude_file_patterns = array_filter( (array) $exclude_file_patterns ) )
			$this->exclude_file_patterns = $exclude_file_patterns;
		/**
		 * Exclude Directories
		 */
		if ( $exclude_dirs = array_filter( (array) $exclude_dirs ) )
			$this->exclude_dirs = $exclude_dirs;
	}

	/**
	 * @access private
	 */
	private function setIterator( $path, $recursive ) {
		$flags = \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_FILEINFO;
		if ( $recursive )
			$this->iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $path, $flags ) );
		else
			$this->iterator = new \FilesystemIterator( $path, $flags );
	}

	/**
	 * @access private
	 */
	private function includeFiles() {
		foreach ( $this->iterator as $path => $fileinfo ) {
			if ( $this->exclude_dirs ) {
				foreach ( $this->exclude_dirs as $dir ) {
					if ( strpos( $path, $dir ) !== false )
						continue 2;
				}
			}
			if ( $fileinfo->getExtension() !== 'php' || in_array( $path, $this->exclude_files ) )
				continue;
			if ( $this->exclude_file_patterns ) {
				$basename = $fileinfo->getBasename();
				foreach ( $this->exclude_file_patterns as $pattern ) {
					if ( preg_match( $pattern, $basename ) )
						continue 2;
				}
			}
			require_once $path;
		}
	}

	/**
	 * Return options arguments filter definition
	 *
	 * @access private
	 *
	 * @return array
	 */
	private static function getOptDef() {
		static $def;
		if ( ! isset( $def ) ) {
			$fileFilter = function( $file ) {
				return filter_var( $file ) ? realpath( $file ) : false;
			};
			$dirFilter = function( $dir ) use ( $fileFilter ) {
				if ( ! $dir = $fileFilter( $dir ) )
					return false;
				return is_dir( $dir ) ? $dir . DIRECTORY_SEPARATOR : false;
			};
			$regexpFilter = function( $pattern ) {
				return @preg_match( $pattern, '' ) !== false ? $pattern : false;
			};
			$def = [
				'exclude_files' => [ 'filter' => \FILTER_CALLBACK, 'options' => $fileFilter ],
				'exclude_file_patterns' => [ 'filter' => \FILTER_CALLBACK, 'options' => $regexpFilter ],
				'exclude_dirs' => [ 'filter' => \FILTER_CALLBACK, 'options' => $dirFilter ],
			];
		}
		return $def;
	}

}
```

今回の場合はテーマディレクトリー内のfunctionsというディレクトリーを読み込んでます。

```php:functions.php
require_once( 'path/to/fileloader.php' );

add_action( 'after_setup_theme', function() {
	FileLoader::init( trailingslashit( get_stylesheet_directory() ) . 'functions/', true );
} );
```

第二引数を省略する、あるいは`false`にすると再帰的に読み込まなくなります。
また、除外したいファイルが有る場合は、

```php
$options = [];

/**
 * 絶対パスで指定
 */
$options['exclude_files'] = trailingslashit( get_stylesheet_directory() ) . 'functions/inc/loop-custom.php';

/**
 * 正規表現で指定 ※処理が重くなるので非推奨
 */
$options['exclude_file_patterns'] = '/\Aloop-\w+/';

/**
 * ディレクトリー単位で指定 ※処理が重くなるので非推奨
 */
$options['exclude_dirs'] = trailingslashit( get_stylesheet_directory() ) . 'functions/inc/';

/**
 * 実行
 */
FileLoader::init( trailingslashit( get_stylesheet_directory() ) . 'functions/', true, $options );
```

と、除外する条件の配列を第三引数に与えて下さい。（各キーについては配列OK）

参考エントリー
- [（PHPで）指定ディレクトリ以下を全部チェックしてファイル一覧を取得する方法 : アシアルブログ](http://blog.asial.co.jp/1250)
- [WordPress でスニペットを簡単に管理する方法 : dogmap.jp](http://dogmap.jp/2011/04/19/wordpress-managed-snippet/)
- [ディレクトリの配下にあるファイル一覧を再帰的に表示する Kaname's hackday](http://kanamehackday.blog17.fc2.com/blog-entry-245.html)