HEX
Server: Apache/2.4.41 (FreeBSD) OpenSSL/1.0.2s mod_fcgid/2.3.9
System: FreeBSD salazo 12.0-RELEASE-p1303-ZFS hostBSD 12.0-RELEASE-p1303-ZFS DMR amd64
User: admin (1000)
PHP: 7.4.3
Disabled: NONE
Upload Files
File: /usr/local/www/apache24/noexec/open-the-door/wp-content/plugins/trustedblogs/class.curl.php
<?php

namespace Curl;

class Curl {
	const VERSION = '3.6.6';
	const DEFAULT_TIMEOUT = 30;

	public $curl;
	public $id = null;

	public $error = false;
	public $error_code = 0;
	public $error_message = null;

	public $curl_error = false;
	public $curl_error_code = 0;
	public $curl_error_message = null;

	public $http_error = false;
	public $http_status_code = 0;
	public $http_error_message = null;

	public $base_url = null;
	public $url = null;
	public $request_headers = null;
	public $response_headers = null;
	public $raw_response_headers = '';
	public $response = null;
	public $raw_response = null;

	public $before_send_function = null;
	public $download_complete_function = null;
	private $success_function = null;
	private $error_function = null;
	private $complete_function = null;

	private $cookies = array();
	private $headers = array();
	private $options = array();

	private $json_decoder = null;
	private $json_pattern = '/^(?:application|text)\/(?:[a-z]+(?:[\.-][0-9a-z]+){0,}[\+\.]|x-)?json(?:-[a-z]+)?/i';
	private $xml_pattern = '~^(?:text/|application/(?:atom\+|rss\+)?)xml~i';

	/**
	 * Construct
	 *
	 * @access public
	 *
	 * @param  $base_url
	 *
	 * @throws \ErrorException
	 */
	public function __construct( $base_url = null ) {
		if ( ! extension_loaded( 'curl' ) ) {
			throw new \ErrorException( 'cURL library is not loaded' );
		}

		$this->curl = curl_init();
		$this->id   = 1;
		$this->setDefaultUserAgent();
		$this->setDefaultJsonDecoder();
		$this->setDefaultTimeout();
		$this->setOpt( CURLINFO_HEADER_OUT, true );
		$this->setOpt( CURLOPT_HEADERFUNCTION, array( $this, 'headerCallback' ) );
		$this->setOpt( CURLOPT_RETURNTRANSFER, true );
		$this->headers = new CaseInsensitiveArray();
		$this->setURL( $base_url );
	}

	/**
	 * Before Send
	 *
	 * @access public
	 *
	 * @param  $callback
	 */
	public function beforeSend( $callback ) {
		$this->before_send_function = $callback;
	}

	/**
	 * Build Post Data
	 *
	 * @access public
	 *
	 * @param  $data
	 *
	 * @return array|string
	 */
	public function buildPostData( $data ) {
		if ( is_array( $data ) ) {
			if ( self::is_array_multidim( $data ) ) {
				if ( isset( $this->headers['Content-Type'] ) &&
				     preg_match( $this->json_pattern, $this->headers['Content-Type'] )
				) {
					$json_str = json_encode( $data );
					if ( ! ( $json_str === false ) ) {
						$data = $json_str;
					}
				} else {
					$data = self::http_build_multi_query( $data );
				}
			} else {
				$binary_data = false;
				foreach ( $data as $key => $value ) {
					// Fix "Notice: Array to string conversion" when $value in
					// curl_setopt($ch, CURLOPT_POSTFIELDS, $value) is an array
					// that contains an empty array.
					if ( is_array( $value ) && empty( $value ) ) {
						$data[ $key ] = '';
						// Fix "curl_setopt(): The usage of the @filename API for
						// file uploading is deprecated. Please use the CURLFile
						// class instead".
					} elseif ( is_string( $value ) && strpos( $value, '@' ) === 0 ) {
						$binary_data = true;
						if ( class_exists( 'CURLFile' ) ) {
							$data[ $key ] = new \CURLFile( substr( $value, 1 ) );
						}
					} elseif ( $value instanceof \CURLFile ) {
						$binary_data = true;
					}
				}

				if ( ! $binary_data ) {
					if ( isset( $this->headers['Content-Type'] ) &&
					     preg_match( $this->json_pattern, $this->headers['Content-Type'] )
					) {
						$json_str = json_encode( $data );
						if ( ! ( $json_str === false ) ) {
							$data = $json_str;
						}
					} else {
						$data = http_build_query( $data, '', '&' );
					}
				}
			}
		}

		return $data;
	}

	/**
	 * Call
	 *
	 * @access public
	 */
	public function call() {
		$args     = func_get_args();
		$function = array_shift( $args );
		if ( is_callable( $function ) ) {
			array_unshift( $args, $this );
			call_user_func_array( $function, $args );
		}
	}

	/**
	 * Close
	 *
	 * @access public
	 */
	public function close() {
		if ( is_resource( $this->curl ) ) {
			curl_close( $this->curl );
		}
		$this->options      = null;
		$this->json_decoder = null;
	}

	/**
	 * Complete
	 *
	 * @access public
	 *
	 * @param  $callback
	 */
	public function complete( $callback ) {
		$this->complete_function = $callback;
	}

	/**
	 * Progress
	 *
	 * @access public
	 *
	 * @param  $callback
	 */
	public function progress( $callback ) {
		$this->setOpt( CURLOPT_PROGRESSFUNCTION, $callback );
		$this->setOpt( CURLOPT_NOPROGRESS, false );
	}

	/**
	 * Delete
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $query_parameters
	 * @param  $data
	 *
	 * @return string
	 */
	public function delete( $url, $query_parameters = array(), $data = array() ) {
		if ( is_array( $url ) ) {
			$data             = $query_parameters;
			$query_parameters = $url;
			$url              = $this->base_url;
		}

		$this->setURL( $url, $query_parameters );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'DELETE' );
		$this->setOpt( CURLOPT_POSTFIELDS, $this->buildPostData( $data ) );

		return $this->exec();
	}

	/**
	 * Download Complete
	 *
	 * @access public
	 *
	 * @param  $fh
	 */
	public function downloadComplete( $fh ) {
		if ( ! $this->error && $this->download_complete_function ) {
			rewind( $fh );
			$this->call( $this->download_complete_function, $fh );
			$this->download_complete_function = null;
		}

		if ( is_resource( $fh ) ) {
			fclose( $fh );
		}

		// Fix "PHP Notice: Use of undefined constant STDOUT" when reading the
		// PHP script from stdin. Using null causes "Warning: curl_setopt():
		// supplied argument is not a valid File-Handle resource".
		if ( ! defined( 'STDOUT' ) ) {
			define( 'STDOUT', fopen( 'php://stdout', 'w' ) );
		}

		// Reset CURLOPT_FILE with STDOUT to avoid: "curl_exec(): CURLOPT_FILE
		// resource has gone away, resetting to default".
		$this->setOpt( CURLOPT_FILE, STDOUT );

		// Reset CURLOPT_RETURNTRANSFER to tell cURL to return subsequent
		// responses as the return value of curl_exec(). Without this,
		// curl_exec() will revert to returning boolean values.
		$this->setOpt( CURLOPT_RETURNTRANSFER, true );
	}

	/**
	 * Download
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $mixed_filename
	 *
	 * @return boolean
	 */
	public function download( $url, $mixed_filename ) {
		if ( is_callable( $mixed_filename ) ) {
			$this->download_complete_function = $mixed_filename;
			$fh                               = tmpfile();
		} else {
			$filename = $mixed_filename;
			$fh       = fopen( $filename, 'wb' );
		}

		$this->setOpt( CURLOPT_FILE, $fh );
		$this->get( $url );
		$this->downloadComplete( $fh );

		return ! $this->error;
	}

	/**
	 * Error
	 *
	 * @access public
	 *
	 * @param  $callback
	 */
	public function error( $callback ) {
		$this->error_function = $callback;
	}

	/**
	 * Exec
	 *
	 * @access public
	 *
	 * @param  $ch
	 *
	 * @return string
	 */
	public function exec( $ch = null ) {
		if ( ! ( $ch === null ) ) {
			$this->raw_response = curl_multi_getcontent( $ch );
		} else {
			$this->call( $this->before_send_function );
			$this->raw_response    = curl_exec( $this->curl );
			$this->curl_error_code = curl_errno( $this->curl );
		}

		$this->curl_error_message = curl_error( $this->curl );
		$this->curl_error         = ! ( $this->curl_error_code === 0 );
		$this->http_status_code   = curl_getinfo( $this->curl, CURLINFO_HTTP_CODE );
		$this->http_error         = in_array( floor( $this->http_status_code / 100 ), array( 4, 5 ) );
		$this->error              = $this->curl_error || $this->http_error;
		$this->error_code         = $this->error ? ( $this->curl_error ? $this->curl_error_code : $this->http_status_code ) : 0;

		$this->request_headers  = $this->parseRequestHeaders( curl_getinfo( $this->curl, CURLINFO_HEADER_OUT ) );
		$this->response_headers = $this->parseResponseHeaders( $this->raw_response_headers );
		list( $this->response, $this->raw_response ) = $this->parseResponse( $this->response_headers, $this->raw_response );

		$this->http_error_message = '';
		if ( $this->error ) {
			if ( isset( $this->response_headers['Status-Line'] ) ) {
				$this->http_error_message = $this->response_headers['Status-Line'];
			}
		}
		$this->error_message = $this->curl_error ? $this->curl_error_message : $this->http_error_message;

		if ( ! $this->error ) {
			$this->call( $this->success_function );
		} else {
			$this->call( $this->error_function );
		}

		$this->call( $this->complete_function );

		return $this->response;
	}

	/**
	 * Get
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function get( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}
		$this->setURL( $url, $data );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'GET' );
		$this->setOpt( CURLOPT_HTTPGET, true );

		return $this->exec();
	}

	/**
	 * Get Opt
	 *
	 * @access public
	 *
	 * @param  $option
	 *
	 * @return mixed
	 */
	public function getOpt( $option ) {
		return $this->options[ $option ];
	}

	/**
	 * Head
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function head( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}
		$this->setURL( $url, $data );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'HEAD' );
		$this->setOpt( CURLOPT_NOBODY, true );

		return $this->exec();
	}

	/**
	 * Header Callback
	 *
	 * @access public
	 *
	 * @param  $ch
	 * @param  $header
	 *
	 * @return integer
	 */
	public function headerCallback( $ch, $header ) {
		$this->raw_response_headers .= $header;

		return strlen( $header );
	}

	/**
	 * Options
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function options( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}
		$this->setURL( $url, $data );
		$this->unsetHeader( 'Content-Length' );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'OPTIONS' );

		return $this->exec();
	}

	/**
	 * Patch
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function patch( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}
		$this->setURL( $url );
		$this->unsetHeader( 'Content-Length' );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'PATCH' );
		$this->setOpt( CURLOPT_POSTFIELDS, $data );

		return $this->exec();
	}

	/**
	 * Post
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function post( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}

		if ( is_array( $data ) && empty( $data ) ) {
			$this->unsetHeader( 'Content-Length' );
		}

		$this->setURL( $url );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'POST' );
		$this->setOpt( CURLOPT_POST, true );
		$this->setOpt( CURLOPT_POSTFIELDS, $this->buildPostData( $data ) );

		return $this->exec();
	}

	/**
	 * Put
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	public function put( $url, $data = array() ) {
		if ( is_array( $url ) ) {
			$data = $url;
			$url  = $this->base_url;
		}
		$this->setURL( $url );
		$this->setOpt( CURLOPT_CUSTOMREQUEST, 'PUT' );
		$put_data = $this->buildPostData( $data );
		if ( empty( $this->options[ CURLOPT_INFILE ] ) && empty( $this->options[ CURLOPT_INFILESIZE ] ) ) {
			$this->setHeader( 'Content-Length', strlen( $put_data ) );
		}
		$this->setOpt( CURLOPT_POSTFIELDS, $put_data );

		return $this->exec();
	}

	/**
	 * Set Basic Authentication
	 *
	 * @access public
	 *
	 * @param  $username
	 * @param  $password
	 */
	public function setBasicAuthentication( $username, $password = '' ) {
		$this->setOpt( CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
		$this->setOpt( CURLOPT_USERPWD, $username . ':' . $password );
	}

	/**
	 * Set Digest Authentication
	 *
	 * @access public
	 *
	 * @param  $username
	 * @param  $password
	 */
	public function setDigestAuthentication( $username, $password = '' ) {
		$this->setOpt( CURLOPT_HTTPAUTH, CURLAUTH_DIGEST );
		$this->setOpt( CURLOPT_USERPWD, $username . ':' . $password );
	}

	/**
	 * Set Cookie
	 *
	 * @access public
	 *
	 * @param  $key
	 * @param  $value
	 */
	public function setCookie( $key, $value ) {
		$this->cookies[ $key ] = $value;
		$this->setOpt( CURLOPT_COOKIE, str_replace( ' ', '%20', urldecode( http_build_query( $this->cookies, '', '; ' ) ) ) );
	}

	/**
	 * Set Port
	 *
	 * @access public
	 *
	 * @param  $port
	 */
	public function setPort( $port ) {
		$this->setOpt( CURLOPT_PORT, intval( $port ) );
	}

	/**
	 * Set Connect Timeout
	 *
	 * @access public
	 *
	 * @param  $seconds
	 */
	public function setConnectTimeout( $seconds ) {
		$this->setOpt( CURLOPT_CONNECTTIMEOUT, $seconds );
	}

	/**
	 * Set Cookie File
	 *
	 * @access public
	 *
	 * @param  $cookie_file
	 */
	public function setCookieFile( $cookie_file ) {
		$this->setOpt( CURLOPT_COOKIEFILE, $cookie_file );
	}

	/**
	 * Set Cookie Jar
	 *
	 * @access public
	 *
	 * @param  $cookie_jar
	 */
	public function setCookieJar( $cookie_jar ) {
		$this->setOpt( CURLOPT_COOKIEJAR, $cookie_jar );
	}

	/**
	 * Set Default JSON Decoder
	 *
	 * @access public
	 */
	public function setDefaultJsonDecoder() {
		$this->json_decoder = function ( $response ) {
			$json_obj = json_decode( $response, false );
			if ( ! ( $json_obj === null ) ) {
				$response = $json_obj;
			}

			return $response;
		};
	}

	/**
	 * Set Default Timeout
	 *
	 * @access public
	 */
	public function setDefaultTimeout() {
		$this->setTimeout( self::DEFAULT_TIMEOUT );
	}

	/**
	 * Set Default User Agent
	 *
	 * @access public
	 */
	public function setDefaultUserAgent() {
		$user_agent = 'PHP-Curl-Class/' . self::VERSION . ' (+https://github.com/php-curl-class/php-curl-class)';
		$user_agent .= ' PHP/' . PHP_VERSION;
		$curl_version = curl_version();
		$user_agent .= ' curl/' . $curl_version['version'];
		$this->setUserAgent( $user_agent );
	}

	/**
	 * Set Header
	 *
	 * @access public
	 *
	 * @param  $key
	 * @param  $value
	 *
	 * @return string
	 */
	public function setHeader( $key, $value ) {
		$this->headers[ $key ] = $value;
		$headers               = array();
		foreach ( $this->headers as $key => $value ) {
			$headers[ $key ] = $value;
		}
		$this->setOpt( CURLOPT_HTTPHEADER, array_map( function ( $value, $key ) {
			return $key . ': ' . $value;
		}, $headers, array_keys( $headers ) ) );
	}

	/**
	 * Set JSON Decoder
	 *
	 * @access public
	 *
	 * @param  $function
	 */
	public function setJsonDecoder( $function ) {
		if ( is_callable( $function ) ) {
			$this->json_decoder = $function;
		}
	}

	/**
	 * Set Opt
	 *
	 * @access public
	 *
	 * @param  $option
	 * @param  $value
	 *
	 * @return boolean
	 */
	public function setOpt( $option, $value ) {
		$required_options = array(
			CURLINFO_HEADER_OUT    => 'CURLINFO_HEADER_OUT',
			CURLOPT_RETURNTRANSFER => 'CURLOPT_RETURNTRANSFER',
		);

		if ( in_array( $option, array_keys( $required_options ), true ) && ! ( $value === true ) ) {
			trigger_error( $required_options[ $option ] . ' is a required option', E_USER_WARNING );
		}

		$this->options[ $option ] = $value;

		return curl_setopt( $this->curl, $option, $value );
	}

	/**
	 * Set Referer
	 *
	 * @access public
	 *
	 * @param  $referer
	 */
	public function setReferer( $referer ) {
		$this->setReferrer( $referer );
	}

	/**
	 * Set Referrer
	 *
	 * @access public
	 *
	 * @param  $referrer
	 */
	public function setReferrer( $referrer ) {
		$this->setOpt( CURLOPT_REFERER, $referrer );
	}

	/**
	 * Set Timeout
	 *
	 * @access public
	 *
	 * @param  $seconds
	 */
	public function setTimeout( $seconds ) {
		$this->setOpt( CURLOPT_TIMEOUT, $seconds );
	}

	/**
	 * Set Url
	 *
	 * @access public
	 *
	 * @param  $url
	 * @param  $data
	 */
	public function setURL( $url, $data = array() ) {
		$this->base_url = $url;
		$this->url      = $this->buildURL( $url, $data );
		$this->setOpt( CURLOPT_URL, $this->url );
	}

	/**
	 * Set User Agent
	 *
	 * @access public
	 *
	 * @param  $user_agent
	 */
	public function setUserAgent( $user_agent ) {
		$this->setOpt( CURLOPT_USERAGENT, $user_agent );
	}

	/**
	 * Success
	 *
	 * @access public
	 *
	 * @param  $callback
	 */
	public function success( $callback ) {
		$this->success_function = $callback;
	}

	/**
	 * Unset Header
	 *
	 * @access public
	 *
	 * @param  $key
	 */
	public function unsetHeader( $key ) {
		$this->setHeader( $key, '' );
		unset( $this->headers[ $key ] );
	}

	/**
	 * Verbose
	 *
	 * @access public
	 *
	 * @param  $on
	 */
	public function verbose( $on = true ) {
		$this->setOpt( CURLOPT_VERBOSE, $on );
	}

	/**
	 * Destruct
	 *
	 * @access public
	 */
	public function __destruct() {
		$this->close();
	}

	/**
	 * Build Url
	 *
	 * @access private
	 *
	 * @param  $url
	 * @param  $data
	 *
	 * @return string
	 */
	private function buildURL( $url, $data = array() ) {
		return $url . ( empty( $data ) ? '' : '?' . http_build_query( $data ) );
	}

	/**
	 * Parse Headers
	 *
	 * @access private
	 *
	 * @param  $raw_headers
	 *
	 * @return array
	 */
	private function parseHeaders( $raw_headers ) {
		$raw_headers  = preg_split( '/\r\n/', $raw_headers, null, PREG_SPLIT_NO_EMPTY );
		$http_headers = new CaseInsensitiveArray();

		$raw_headers_count = count( $raw_headers );
		for ( $i = 1; $i < $raw_headers_count; $i ++ ) {
			list( $key, $value ) = explode( ':', $raw_headers[ $i ], 2 );
			$key   = trim( $key );
			$value = trim( $value );
			// Use isset() as array_key_exists() and ArrayAccess are not compatible.
			if ( isset( $http_headers[ $key ] ) ) {
				$http_headers[ $key ] .= ',' . $value;
			} else {
				$http_headers[ $key ] = $value;
			}
		}

		return array( isset( $raw_headers['0'] ) ? $raw_headers['0'] : '', $http_headers );
	}

	/**
	 * Parse Request Headers
	 *
	 * @access private
	 *
	 * @param  $raw_headers
	 *
	 * @return array
	 */
	private function parseRequestHeaders( $raw_headers ) {
		$request_headers = new CaseInsensitiveArray();
		list( $first_line, $headers ) = $this->parseHeaders( $raw_headers );
		$request_headers['Request-Line'] = $first_line;
		foreach ( $headers as $key => $value ) {
			$request_headers[ $key ] = $value;
		}

		return $request_headers;
	}

	/**
	 * Parse Response
	 *
	 * @access private
	 *
	 * @param  $response_headers
	 * @param  $raw_response
	 *
	 * @return array
	 */
	private function parseResponse( $response_headers, $raw_response ) {
		$response = $raw_response;
		if ( isset( $response_headers['Content-Type'] ) ) {
			if ( preg_match( $this->json_pattern, $response_headers['Content-Type'] ) ) {
				$json_decoder = $this->json_decoder;
				if ( is_callable( $json_decoder ) ) {
					$response = $json_decoder( $response );
				}
			} elseif ( preg_match( $this->xml_pattern, $response_headers['Content-Type'] ) ) {
				$xml_obj = @simplexml_load_string( $response );
				if ( ! ( $xml_obj === false ) ) {
					$response = $xml_obj;
				}
			}
		}

		return array( $response, $raw_response );
	}

	/**
	 * Parse Response Headers
	 *
	 * @access private
	 *
	 * @param  $raw_response_headers
	 *
	 * @return array
	 */
	private function parseResponseHeaders( $raw_response_headers ) {
		$response_header_array = explode( "\r\n\r\n", $raw_response_headers );
		$response_header       = '';
		for ( $i = count( $response_header_array ) - 1; $i >= 0; $i -- ) {
			if ( stripos( $response_header_array[ $i ], 'HTTP/' ) === 0 ) {
				$response_header = $response_header_array[ $i ];
				break;
			}
		}

		$response_headers = new CaseInsensitiveArray();
		list( $first_line, $headers ) = $this->parseHeaders( $response_header );
		$response_headers['Status-Line'] = $first_line;
		foreach ( $headers as $key => $value ) {
			$response_headers[ $key ] = $value;
		}

		return $response_headers;
	}

	/**
	 * Http Build Multi Query
	 *
	 * @access public
	 *
	 * @param  $data
	 * @param  $key
	 *
	 * @return string
	 */
	public static function http_build_multi_query( $data, $key = null ) {
		$query = array();

		if ( empty( $data ) ) {
			return $key . '=';
		}

		$is_array_assoc = self::is_array_assoc( $data );

		foreach ( $data as $k => $value ) {
			if ( is_string( $value ) || is_numeric( $value ) ) {
				$brackets = $is_array_assoc ? '[' . $k . ']' : '[]';
				$query[]  = urlencode( $key === null ? $k : $key . $brackets ) . '=' . rawurlencode( $value );
			} elseif ( is_array( $value ) ) {
				$nested  = $key === null ? $k : $key . '[' . $k . ']';
				$query[] = self::http_build_multi_query( $value, $nested );
			}
		}

		return implode( '&', $query );
	}

	/**
	 * Is Array Assoc
	 *
	 * @access public
	 *
	 * @param  $array
	 *
	 * @return boolean
	 */
	public static function is_array_assoc( $array ) {
		return (bool) count( array_filter( array_keys( $array ), 'is_string' ) );
	}

	/**
	 * Is Array Multidim
	 *
	 * @access public
	 *
	 * @param  $array
	 *
	 * @return boolean
	 */
	public static function is_array_multidim( $array ) {
		if ( ! is_array( $array ) ) {
			return false;
		}

		return (bool) count( array_filter( $array, 'is_array' ) );
	}
}

class CaseInsensitiveArray implements \ArrayAccess, \Countable, \Iterator {
	private $container = array();

	public function offsetSet( $offset, $value ) {
		if ( $offset === null ) {
			$this->container[] = $value;
		} else {
			$index = array_search( strtolower( $offset ), array_keys( array_change_key_case( $this->container, CASE_LOWER ) ) );
			if ( ! ( $index === false ) ) {
				$keys = array_keys( $this->container );
				unset( $this->container[ $keys[ $index ] ] );
			}
			$this->container[ $offset ] = $value;
		}
	}

	public function offsetExists( $offset ) {
		return array_key_exists( strtolower( $offset ), array_change_key_case( $this->container, CASE_LOWER ) );
	}

	public function offsetUnset( $offset ) {
		unset( $this->container[ $offset ] );
	}

	public function offsetGet( $offset ) {
		$index = array_search( strtolower( $offset ), array_keys( array_change_key_case( $this->container, CASE_LOWER ) ) );
		if ( $index === false ) {
			return null;
		}

		$values = array_values( $this->container );

		return $values[ $index ];
	}

	public function count() {
		return count( $this->container );
	}

	public function current() {
		return current( $this->container );
	}

	public function next() {
		return next( $this->container );
	}

	public function key() {
		return key( $this->container );
	}

	public function valid() {
		return ! ( $this->current() === false );
	}

	public function rewind() {
		reset( $this->container );
	}
}