locate_plugin(); $this->slug = $location['dir_basename']; $this->dir_path = $location['dir_path']; $this->dir_url = $location['dir_url']; spl_autoload_register( array( $this, 'autoload' ) ); $this->add_doc_hooks(); } /** * Plugin_Base destructor. */ public function __destruct() { $this->remove_doc_hooks(); } /** * Get reflection object for this class. * * @return \ReflectionObject */ public function get_object_reflection() { static $reflection; if ( empty( $reflection ) ) { $reflection = new \ReflectionObject( $this ); } return $reflection; } /** * Autoload for classes that are in the same namespace as $this. * * @param string $class Class name. * @return void */ public function autoload( $class ) { if ( ! isset( $this->autoload_matches_cache[ $class ] ) ) { if ( ! preg_match( '/^(?P.+)\\\\(?P[^\\\\]+)$/', $class, $matches ) ) { $matches = false; } $this->autoload_matches_cache[ $class ] = $matches; } else { $matches = $this->autoload_matches_cache[ $class ]; } if ( empty( $matches ) ) { return; } if ( $this->get_object_reflection()->getNamespaceName() !== $matches['namespace'] ) { return; } $class_name = $matches['class']; $class_path = \trailingslashit( $this->dir_path ); if ( $this->autoload_class_dir ) { $class_path .= \trailingslashit( $this->autoload_class_dir ); } $class_path .= sprintf( 'class-%s.php', strtolower( str_replace( '_', '-', $class_name ) ) ); if ( is_readable( $class_path ) ) { require_once $class_path; } } /** * Version of plugin_dir_url() which works for plugins installed in the plugins directory, * and for plugins bundled with themes. * * @return array */ public function locate_plugin() { $dir_url = trailingslashit( substr( plugins_url( '', __FILE__ ), 0, - 4 ) ); $dir_path = trailingslashit( substr( __DIR__, 0, - 4 ) ); $dir_basename = basename( $dir_path ); return compact( 'dir_url', 'dir_path', 'dir_basename' ); } /** * Hooks a function on to a specific filter. * * @param string $name The hook name. * @param array $callback The class object and method. * @param array $args An array with priority and arg_count. * * @return mixed */ public function add_filter( $name, $callback, $args = array() ) { // Merge defaults. $args = array_merge( array( 'priority' => 10, 'arg_count' => PHP_INT_MAX, ), $args ); return $this->add_hook( 'filter', $name, $callback, $args ); } /** * Hooks a function on to a specific action. * * @param string $name The hook name. * @param array $callback The class object and method. * @param array $args An array with priority and arg_count. * * @return mixed */ public function add_action( $name, $callback, $args = array() ) { // Merge defaults. $args = array_merge( array( 'priority' => 1, 'arg_count' => PHP_INT_MAX, ), $args ); return $this->add_hook( 'action', $name, $callback, $args ); } /** * Hooks a function on to a specific shortcode. * * @param string $name The shortcode name. * @param array $callback The class object and method. * * @return mixed */ public function add_shortcode( $name, $callback ) { return $this->add_hook( 'shortcode', $name, $callback ); } /** * Hooks a function on to a specific action/filter. * * @param string $type The hook type. Options are action/filter. * @param string $name The hook name. * @param array $callback The class object and method. * @param array $args An array with priority and arg_count. * * @return mixed */ protected function add_hook( $type, $name, $callback, $args = array() ) { $priority = isset( $args['priority'] ) ? $args['priority'] : 10; $arg_count = isset( $args['arg_count'] ) ? $args['arg_count'] : PHP_INT_MAX; $fn = sprintf( '\add_%s', $type ); $retval = \call_user_func( $fn, $name, $callback, $priority, $arg_count ); return $retval; } /** * Add actions/filters/shortcodes from the methods of a class based on DocBlocks. * * @param object $object The class object. */ public function add_doc_hooks( $object = null ) { if ( is_null( $object ) ) { $object = $this; } $class_name = get_class( $object ); if ( isset( $this->called_doc_hooks[ $class_name ] ) ) { $notice = sprintf( 'The add_doc_hooks method was already called on %s. Note that the Plugin_Base constructor automatically calls this method.', $class_name ); // @codingStandardsIgnoreStart trigger_error( esc_html( $notice ), \E_USER_NOTICE ); // @codingStandardsIgnoreEnd return; } $this->called_doc_hooks[ $class_name ] = true; $reflector = new \ReflectionObject( $object ); foreach ( $reflector->getMethods() as $method ) { $doc = $method->getDocComment(); $arg_count = $method->getNumberOfParameters(); if ( preg_match_all( '#\* @(?Pfilter|action|shortcode)\s+(?P[a-z0-9\-\._]+)(?:,\s+(?P\d+))?#', $doc, $matches, PREG_SET_ORDER ) ) { foreach ( $matches as $match ) { $type = $match['type']; $name = $match['name']; $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); $callback = array( $object, $method->getName() ); call_user_func( array( $this, "add_{$type}" ), $name, $callback, compact( 'priority', 'arg_count' ) ); } } } } /** * Removes the added DocBlock hooks. * * @param object $object The class object. */ public function remove_doc_hooks( $object = null ) { if ( is_null( $object ) ) { $object = $this; } $class_name = get_class( $object ); $reflector = new \ReflectionObject( $object ); foreach ( $reflector->getMethods() as $method ) { $doc = $method->getDocComment(); if ( preg_match_all( '#\* @(?Pfilter|action|shortcode)\s+(?P[a-z0-9\-\._]+)(?:,\s+(?P\d+))?#', $doc, $matches, PREG_SET_ORDER ) ) { foreach ( $matches as $match ) { $type = $match['type']; $name = $match['name']; $priority = empty( $match['priority'] ) ? 10 : intval( $match['priority'] ); $callback = array( $object, $method->getName() ); call_user_func( "remove_{$type}", $name, $callback, $priority ); } } } unset( $this->called_doc_hooks[ $class_name ] ); } }