posted_on()`. */ class Template_Tags { /** * Associative array of all available template tags. * * Method names are the keys, their callback information the values. * * @var array */ protected $template_tags = array(); /** * Constructor. * * Sets the theme components. * * @param array $components Optional. List of theme templating components. Each of these must implement the * Templating_Component_Interface interface. * * @throws InvalidArgumentException Thrown if one of the $components does not implement * Templating_Component_Interface. */ public function __construct( array $components = array() ) { // Set the template tags for the components. foreach ( $components as $component ) { // Bail if a templating component is invalid. if ( ! $component instanceof Templating_Component_Interface ) { throw new InvalidArgumentException( sprintf( /* translators: 1: classname/type of the variable, 2: interface name */ esc_html__( 'The theme templating component %1$s does not implement the %2$s interface.', 'buddyx' ), esc_html( gettype( $component ) ), Templating_Component_Interface::class ) ); } $this->set_template_tags( $component ); } } /** * Magic call method. * * Will proxy to the template tag $method, unless it is not available, in which case an exception will be thrown. * * @param string $method Template tag name. * @param array $args Template tag arguments. * @return mixed Template tag result, or null if template tag only outputs markup. * * @throws BadMethodCallException Thrown if the template tag does not exist. */ public function __call( string $method, array $args ) { if ( ! isset( $this->template_tags[ $method ] ) ) { throw new BadMethodCallException( sprintf( /* translators: %s: template tag name */ esc_html__( 'The template tag %s does not exist.', 'buddyx' ), 'buddyx()->' . esc_html( $method ) . '()' ) ); } return call_user_func_array( $this->template_tags[ $method ]['callback'], $args ); } /** * Sets template tags for a given theme templating component. * * @param Templating_Component_Interface $component Theme templating component. * * @throws InvalidArgumentException Thrown when one of the template tags is invalid. * @throws RuntimeException Thrown when one of the template tags conflicts with an existing one. */ protected function set_template_tags( Templating_Component_Interface $component ) { $tags = $component->template_tags(); foreach ( $tags as $method_name => $callback ) { if ( is_callable( $callback ) ) { $callback = array( 'callback' => $callback ); } if ( ! is_array( $callback ) || ! isset( $callback['callback'] ) ) { throw new InvalidArgumentException( sprintf( /* translators: 1: template tag method name, 2: component class name */ esc_html__( 'The template tag method %1$s registered by theme component %2$s must either be a callable or an array.', 'buddyx' ), esc_html( $method_name ), esc_html( get_class( $component ) ) ) ); } if ( isset( $this->template_tags[ $method_name ] ) ) { throw new RuntimeException( sprintf( /* translators: 1: template tag method name, 2: component class name */ esc_html__( 'The template tag method %1$s registered by theme component %2$s conflicts with an already registered template tag of the same name.', 'buddyx' ), esc_html( $method_name ), esc_html( get_class( $component ) ) ) ); } $this->template_tags[ $method_name ] = $callback; } } /** * Gets the theme version. * * @return string Theme version number. */ public function get_version(): string { static $theme_version = null; if ( null === $theme_version ) { $theme_version = wp_get_theme( get_template() )->get( 'Version' ); } return $theme_version; } /** * Gets the version for a given asset. * * Returns filemtime when WP_DEBUG is true, otherwise the theme version. * * @param string $filepath Asset file path. * @return string Asset version number. */ public function get_asset_version( string $filepath ): string { if ( WP_DEBUG ) { return (string) filemtime( $filepath ); } return $this->get_version(); } /** * Gets a theme asset from the assets directory. * * @param string $filename The name of the asset file (with extension). * @param string $type The asset type/subdirectory (e.g., 'images', 'svg'). * @param bool $content Whether to return the file contents (true) or URL (false). * @return string|null The asset URL/contents or null if not found. * * @throws RuntimeException If the asset file cannot be read. */ public function get_theme_asset( string $filename, string $type = 'images', bool $content = false ): ?string { $asset_path = get_template_directory() . '/assets/' . trim( $type, '/' ) . '/' . $filename; $asset_uri = get_template_directory_uri() . '/assets/' . trim( $type, '/' ) . '/' . $filename; if ( ! file_exists( $asset_path ) ) { return null; } if ( $content ) { try { // Initialize WordPress Filesystem. global $wp_filesystem; if ( empty( $wp_filesystem ) ) { require_once ABSPATH . '/wp-admin/includes/file.php'; WP_Filesystem(); } if ( ! $wp_filesystem ) { throw new RuntimeException( esc_html__( 'WordPress filesystem is not initialized properly.', 'buddyx' ) ); } $file_contents = $wp_filesystem->get_contents( $asset_path ); if ( false === $file_contents ) { throw new RuntimeException( sprintf( /* translators: %s: asset file path */ esc_html__( 'Error reading asset file: %s', 'buddyx' ), esc_html( $asset_path ) ) ); } return $file_contents; } catch ( \Exception $e ) { throw new RuntimeException( sprintf( /* translators: %s: error message */ esc_html__( 'Failed to get asset contents: %s', 'buddyx' ), esc_html( $e->getMessage() ) ) ); } } return $asset_uri; } }