Undefined constant with debug set to true

You have a lot of issues here.

  • Stay away from defining globals and constants if you can. The global scope is an evil place to be. WordPress has made a huge mess of the global scope already, don’t make it a bigger mess than it already is. You can make use a static variable inside your function/filter

  • Avoid using TEMPLATEPATH. You should use get_template_directory_uri(). IIRC, there was a notice somewhere of future depreciation of TEMPLATEPATH, but citation is needed on this as locate_template() for one still uses TEMPLATEPATH. But anyway, use get_template_path_directory_uri() for parent themes, that is the correct way

  • You would want to use locate_template() to search for a specific template, and not file_exists. Let WordPress handle WordPress stuff. Only use PHP (or for that matter custom SQL) if WordPress does not provide a native function/means of doing something. The native functions are there to take care of other stuff that you might be forgetting about when using custom PHP

    In the new context, if we use locate_template(), we do not need to use get_template_directory_uir(). locate_template() by default handles that section for us. That is why it is important to use native WordPress functions to handle a specific job

    You have a double slash in your generated filenames. Your constant is /single/ and your file name is /single.php, which renders /single//single.php. You need to remove a slash in iether of the two to avoid the duplicate slash

  • single-{$post_type}.php is by default used for custom post types

  • If you are working with the post object of a single post page outside the loop, rather use get_queried_object() which is much more reliable than the $post global.

  • Although your syntax is valid, it is better to use curly brackets ({}) instead of the : and endforeach syntax. This also goes for the syntax of your conditionl statements. I prefer curlies as they are easy to debug should my code fail

  • If you create a custom hierarchy like you have, always set index.php as your ultimate fallback template as index.php is required by all themes

Lets properly rewrite your code (NOTE: I’m using closures which have the downside that it cannot be removed by remove_filter(), so change it to normal spaghetti if you wish.). We will be using the logic inside get_single_template() to help us here

(The code requires PHP 5.4+ and is untested)

add_filter( 'single_template', function ( $template )
{
    // Get the current single post object
    $post = get_queried_object();
    // Set our 'constant' folder path
    $path="single/";

    // Set our variable to hold our templates
    $templates = [];

    // Lets handle the custom post type section
    if ( 'post' !== $post->post_type ) {
        $templates[] = $path . 'single-' . $post->post_type . '-' . $post->post_name . '.php';
        $templates[] = $path . 'single-' . $post->post_type . '.php';
    }

    // Lets handle the post post type stuff
    if ( 'post' === $post->post_type ) {
        // Get the post categories
        $categories = get_the_category( $post->ID );
        // Just for incase, check if we have categories
        if ( $categories ) {
            foreach ( $categories as $category ) {
                // Create possible template names
                $templates[] = $path . 'single-cat-' . $category->slug . '.php';
                $templates[] = $path . 'single-cat-' . $category->term_id . '.php';
            } //endforeach
        } //endif $categories
    } // endif  

    // Set our fallback templates
    $templates[] = $path . 'single.php';
    $templates[] = $path . 'default.php';
    $templates[] = 'index.php';

    /**
     * Now we can search for our templates and load the first one we find
     * We will use the array ability of locate_template here
     */
    $template = locate_template( $templates );

    // Return the template rteurned by locate_template
    return $template;
});

EDIT

The code above is tested and working as expected.

EDIT 2 – Pre PHP 5.4 (T_Rex still roam the darkness)

add_filter( 'single_template', function ( $template )
{
    // Get the current single post object
    $post = get_queried_object();
    // Set our 'constant' folder path
    $path="single/";

    // Set our variable to hold our templates
    $templates = array();

    // Lets handle the custom post type section
    if ( 'post' !== $post->post_type ) {
        $templates[] = $path . 'single-' . $post->post_type . '-' . $post->post_name . '.php';
        $templates[] = $path . 'single-' . $post->post_type . '.php';
    }

    // Lets handle the post post type stuff
    if ( 'post' === $post->post_type ) {
        // Get the post categories
        $categories = get_the_category( $post->ID );
        // Just for incase, check if we have categories
        if ( $categories ) {
            foreach ( $categories as $category ) {
                // Create possible template names
                $templates[] = $path . 'single-cat-' . $category->slug . '.php';
                $templates[] = $path . 'single-cat-' . $category->term_id . '.php';
            } //endforeach
        } //endif $categories
    } // endif  

    // Set our fallback templates
    $templates[] = $path . 'single.php';
    $templates[] = $path . 'default.php';
    $templates[] = 'index.php';

    /**
     * Now we can search for our templates and load the first one we find
     * We will use the array ability of locate_template here
     */
    $template = locate_template( $templates );

    // Return the template rteurned by locate_template
    return $template;
});