# easy-invoice/2.4.1/includes/Helpers/TemplateLocator.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.4.1. 127 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.4.1/code/includes/Helpers/TemplateLocator.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.4.1/raw/includes/Helpers/TemplateLocator.php
- Modified: 2026-09-15T12:31:20+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.4.1/code/includes/Helpers/TemplateLocator.php#L10-L20`.

```php
<?php
/**
 * Theme-overridable template lookup.
 *
 * @package Easy_Invoice
 * @subpackage Helpers
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Resolve a template name to a file, letting the theme override it.
 *
 * Lookup order:
 *   1. {child theme}/easy-invoice/{template}
 *   2. {parent theme}/easy-invoice/{template}
 *   3. {plugin}/templates/{template}
 *
 * This is the same convention WooCommerce established, so anyone who has
 * customised a shop knows what to do: copy the file into the theme, keep the
 * path, edit. Until now every template path in the plugin was hard-coded, so
 * the only way to change what a client saw was to edit the plugin and lose
 * it on the next update.
 *
 * The plugin's own screens (the admin pages under templates/admin) go
 * through this too, but a theme has no business overriding those; only the
 * public documents, the design templates and the PDFs are documented as
 * overridable.
 *
 * @param string $template Path relative to the plugin's templates/ directory,
 *                         e.g. 'document/single.php'.
 * @param array  $args     Context, passed to the filter for callers that want
 *                         to switch templates on it.
 * @return string Absolute path, or '' when nothing exists anywhere.
 */
function easy_invoice_locate_template( string $template, array $args = [] ): string {
    $template = ltrim( str_replace( '\\', '/', $template ), '/' );

    // Never let a template name climb out of the directories we search.
    if ( '' === $template || false !== strpos( $template, '..' ) ) {
        return '';
    }

    /**
     * Filter the folder inside a theme that holds overrides.
     *
     * @param string $dir Folder name, default 'easy-invoice'.
     */
    $theme_dir = trim( (string) apply_filters( 'easy_invoice_template_directory', 'easy-invoice' ), '/' );

    $candidates = [];
    if ( '' !== $theme_dir ) {
        $candidates[] = trailingslashit( get_stylesheet_directory() ) . $theme_dir . '/' . $template;
        if ( get_template_directory() !== get_stylesheet_directory() ) {
            $candidates[] = trailingslashit( get_template_directory() ) . $theme_dir . '/' . $template;
        }
    }
    $candidates[] = EASY_INVOICE_PLUGIN_DIR . 'templates/' . $template;

    $located = '';
    foreach ( $candidates as $candidate ) {
        if ( file_exists( $candidate ) ) {
            $located = $candidate;
            break;
        }
    }

    /**
     * Filter the resolved template path.
     *
     * Lets an addon or a site ship a template somewhere other than the
     * theme. Return '' to declare it missing.
     *
     * @param string $located  Absolute path found, or ''.
     * @param string $template The relative name asked for.
     * @param array  $args     Context passed by the caller.
     */
    $located = (string) apply_filters( 'easy_invoice_locate_template', $located, $template, $args );

    return ( '' !== $located && file_exists( $located ) ) ? $located : '';
}

/**
 * Include a template, exposing $args as variables.
 *
 * @param string $template Relative template name.
 * @param array  $args     Variables the template may read.
 * @return void
 */
function easy_invoice_get_template( string $template, array $args = [] ): void {
    $located = easy_invoice_locate_template( $template, $args );
    if ( '' === $located ) {
        return;
    }

    if ( ! empty( $args ) ) {
        extract( $args, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract -- template context, keys are ours.
    }

    include $located;
}

/**
 * Resolve a design template (the look of an invoice or quote) by name.
 *
 * Falls back through the plugin's 'default' design when the requested one
 * does not exist anywhere, so an invoice that references a design from an
 * addon that has since been switched off still renders.
 *
 * @param string $type 'invoice' or 'quote'.
 * @param string $name Design name, e.g. 'standard'.
 * @return string Absolute path to the design file.
 */
function easy_invoice_design_template( string $type, string $name ): string {
    $folder = ( 'quote' === $type ) ? 'quote-templates' : 'invoice-templates';
    $name   = sanitize_file_name( '' !== $name ? $name : 'standard' );

    $path = easy_invoice_locate_template( $folder . '/' . $name . '.php', [ 'type' => $type, 'design' => $name ] );
    if ( '' === $path ) {
        $path = easy_invoice_locate_template( $folder . '/default.php', [ 'type' => $type, 'design' => 'default' ] );
    }

    return $path;
}

```
