'; * * // Procedural call a la jQuery: * $qp = qp($xml, '#myID'); * $qp->append('')->writeHTML(); * * // Object-oriented version with a factory: * $qp = QueryPath::with($xml)->find('#myID') * $qp->append('')->writeHTML(); * ?> * @endcode * * The above would print (formatted for readability): * @code * * * * * * * * * @endcode * * ## Discovering the Library * * To gain familiarity with QueryPath, the following three API docs are * the best to start with: * *- qp(): This function constructs new queries, and is the starting point * for manipulating a document. htmlqp() is an alias tuned for HTML * documents (especially old HTML), and QueryPath::with(), QueryPath::withXML() * and QueryPath::withHTML() all perform a similar role, but in a purely * object oriented way. *- QueryPath: This is the top-level class for the library. It defines the * main factories and some useful functions. *- QueryPath::Query: This defines all of the functions in QueryPath. When * working with HTML and XML, the QueryPath::DOMQuery is the actual * implementation that you work with. * * Included with the source code for QueryPath is a complete set of unit tests * as well as some example files. Those are good resources for learning about * how to apply QueryPath's tools. The full API documentation can be generated * from these files using Doxygen, or you can view it online at * http://api.querypath.org. * * If you are interested in building extensions for QueryPath, see the * QueryPath and QueryPath::Extension classes. There you will find information on adding * your own tools to QueryPath. * * QueryPath also comes with a full CSS 3 selector implementation (now * with partial support for the current draft of the CSS 4 selector spec). If * you are interested in reusing that in other code, you will want to start * with QueryPath::CSS::EventHandler.php, which is the event interface for the parser. * * All of the code in QueryPath is licensed under an MIT-style license * license. All of the code is Copyright, 2012 by Matt Butcher. * * @author M Butcher * @license MIT * @see QueryPath * @see qp() * @see http://querypath.org The QueryPath home page. * @see http://api.querypath.org An online version of the API docs. * @see http://technosophos.com For how-tos and examples. * @copyright Copyright (c) 2009-2012, Matt Butcher. * @version -UNSTABLE% (3.x.x) * */ use \Masterminds\HTML5; /** * */ class QueryPath { /** * The version string for this version of QueryPath. * * Standard releases will be of the following form: .[.][-STABILITY]. * * Examples: * - 2.0 * - 2.1.1 * - 2.0-alpha1 * * Developer releases will always be of the form dev-. * * @since 2.0 */ const VERSION = '3.0.x'; /** * Major version number. * * Examples: * - 3 * - 4 * * @since 3.0.1 */ const VERSION_MAJOR = 3; /** * This is a stub HTML 4.01 document. * * Using {@link QueryPath::XHTML_STUB} is preferred. * * This is primarily for generating legacy HTML content. Modern web applications * should use QueryPath::XHTML_STUB. * * Use this stub with the HTML familiy of methods (QueryPath::Query::html(), * QueryPath::Query::writeHTML(), QueryPath::Query::innerHTML()). */ const HTML_STUB = ' Untitled '; const HTML5_STUB = ' Untitled '; /** * This is a stub XHTML document. * * Since XHTML is an XML format, you should use XML functions with this document * fragment. For example, you should use {@link xml()}, {@link innerXML()}, and * {@link writeXML()}. * * This can be passed into {@link qp()} to begin a new basic HTML document. * * Example: * @code * $qp = qp(QueryPath::XHTML_STUB); // Creates a new XHTML document * $qp->writeXML(); // Writes the document as well-formed XHTML. * @endcode * @since 2.0 */ const XHTML_STUB = ' Untitled '; public static function with($document = NULL, $selector = NULL, $options = array()) { $qpClass = isset($options['QueryPath_class']) ? $options['QueryPath_class'] : '\QueryPath\DOMQuery'; $qp = new $qpClass($document, $selector, $options); return $qp; } public static function withXML($source = NULL, $selector = NULL, $options = array()) { $options += array( 'use_parser' => 'xml', ); return self::with($source, $selector, $options); } public static function withHTML($source = NULL, $selector = NULL, $options = array()) { // Need a way to force an HTML parse instead of an XML parse when the // doctype is XHTML, since many XHTML documents are not valid XML // (because of coding errors, not by design). $options += array( 'ignore_parser_warnings' => TRUE, 'convert_to_encoding' => 'ISO-8859-1', 'convert_from_encoding' => 'auto', //'replace_entities' => TRUE, 'use_parser' => 'html', // This is stripping actually necessary low ASCII. //'strip_low_ascii' => TRUE, ); return @self::with($source, $selector, $options); } /** * Parse HTML5 documents. * * This uses HTML5-PHP to parse the document. In actuality, this parser does * a fine job with pre-HTML5 documents in most cases, though really old HTML * (like 2.0) may have some substantial quirks. * * Supported Options * Any options supported by HTML5-PHP are allowed here. Additionally, the * following options have meaning to QueryPath. * - QueryPath_class * * * @param mixed $source * A document as an HTML string, or a path/URL. For compatibility with * existing functions, a DOMDocument, SimpleXMLElement, DOMNode or array * of DOMNodes will be passed through as well. However, these types are not * validated in any way. * * @param string $selector * A CSS3 selector. * * @param array $options * An associative array of options, which is passed on into HTML5-PHP. Note * that the standard QueryPath options may be ignored for this function, * since it uses a different parser. * * @return QueryPath */ public static function withHTML5($source = NULL, $selector = NULL, $options = array()) { $qpClass = isset($options['QueryPath_class']) ? $options['QueryPath_class'] : '\QueryPath\DOMQuery'; if(is_string($source)) { $html5 = new HTML5(); if (strpos($source, '<') !== FALSE && strpos($source, '>') !== FALSE) { $source = $html5->loadHTML($source); } else { $source = $html5->load($source); } } $qp = new $qpClass($source, $selector, $options); return $qp; } /** * Enable one or more extensions. * * Extensions provide additional features to QueryPath. To enable and * extension, you can use this method. * * In this example, we enable the QPTPL extension: * @code * * @endcode * * Note that the name is a fully qualified class name. * * We can enable more than one extension at a time like this: * * @code * * @endcode * * @attention If you are not using an autoloader, you will need to * manually `require` or `include` the files that contain the * extensions. * * @param mixed $extensionNames * The name of an extension or an array of extension names. * QueryPath assumes that these are extension class names, * and attempts to register these as QueryPath extensions. */ public static function enable($extensionNames) { if (is_array($extensionNames)) { foreach ($extensionNames as $extension) { \QueryPath\ExtensionRegistry::extend($extension); } } else { \QueryPath\ExtensionRegistry::extend($extensionNames); } } /** * Get a list of all of the enabled extensions. * * This example dumps a list of extensions to standard output: * @code * * @endcode * * @return array * An array of extension names. * * @see QueryPath::ExtensionRegistry */ public static function enabledExtensions() { return \QueryPath\ExtensionRegistry::extensionNames(); } /** * A static function for transforming data into a Data URL. * * This can be used to create Data URLs for injection into CSS, JavaScript, or other * non-XML/HTML content. If you are working with QP objects, you may want to use * dataURL() instead. * * @param mixed $data * The contents to inject as the data. The value can be any one of the following: * - A URL: If this is given, then the subsystem will read the content from that URL. THIS * MUST BE A FULL URL, not a relative path. * - A string of data: If this is given, then the subsystem will encode the string. * - A stream or file handle: If this is given, the stream's contents will be encoded * and inserted as data. * (Note that we make the assumption here that you would never want to set data to be * a URL. If this is an incorrect assumption, file a bug.) * @param string $mime * The MIME type of the document. * @param resource $context * A valid context. Use this only if you need to pass a stream context. This is only necessary * if $data is a URL. (See {@link stream_context_create()}). * @return * An encoded data URL. */ public static function encodeDataURL($data, $mime = 'application/octet-stream', $context = NULL) { if (is_resource($data)) { $data = stream_get_contents($data); } elseif (filter_var($data, FILTER_VALIDATE_URL)) { $data = file_get_contents($data, FALSE, $context); } $encoded = base64_encode($data); return 'data:' . $mime . ';base64,' . $encoded; } }