diff --git a/includes/theme.inc b/includes/theme.inc
index 9b606e9..8563f18 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -1325,27 +1325,30 @@ function drupal_find_theme_templates($cache, $extension, $path) {
   $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : array();
 
   // Escape the periods in the extension.
-  $regex = '/' . str_replace('.', '\.', $extension) . '$/';
+  $regex = '/' . preg_quote($extension, '/') . '$/';
   // Get a listing of all template files in the path to search.
   $files = drupal_system_listing($regex, $path, 'name', 0);
 
   // Find templates that implement registered theme hooks and include that in
   // what is returned so that the registry knows that the theme has this
   // implementation.
-  foreach ($files as $template => $file) {
+  // We can not use drupal_system_listing() with $key = 'name' because it will
+  // only remove the last dot-separated segment of the file name, and theme
+  // engines extensions may contain as many dot-separated segment as they with
+  // (e.g. '.tpl.php', '.html.twig', '.phtml', ...).
+  $patterns = array();
+  foreach ($files as $file) {
     // Ignore sub-theme templates for the current theme.
     if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) {
       continue;
     }
-    // Chop off the remaining extensions if there are any. $template already
-    // has the rightmost extension removed, but there might still be more,
-    // such as with .tpl.php, which still has .tpl in $template at this point.
-    if (($pos = strpos($template, '.')) !== FALSE) {
-      $template = substr($template, 0, $pos);
-    }
-    // Transform - in filenames to _ to match function naming scheme
-    // for the purposes of searching.
-    $hook = strtr($template, '-', '_');
+    // Munge the theme template file extension from the found file name
+    // which gives the logic template name.
+    $template = basename($file->filename, $extension);
+    $patterns[$file->uri] = $template;
+    // Transform - in filenames to _ to match function naming scheme for the
+    // purposes of searching.
+    $hook = strtr($template, array('-' => '_', '.' => '_'));
     if (isset($cache[$hook])) {
       $implementations[$hook] = array(
         'template' => $template,
@@ -1358,24 +1361,25 @@ function drupal_find_theme_templates($cache, $extension, $path) {
   // theme hooks and add those as new registered theme hooks. See
   // drupal_find_theme_functions() for more information about suggestions and
   // the use of 'pattern' and 'base hook'.
-  $patterns = array_keys($files);
   foreach ($cache as $hook => $info) {
     $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
     if (!isset($info['base hook']) && !empty($pattern)) {
       // Transform _ in pattern to - to match file naming scheme
       // for the purposes of searching.
-      $pattern = strtr($pattern, '_', '-');
-
+      $pattern = strtr($pattern, array('_' => '-', '.' => '-'));
+      // preg_grep() allows the module defined 'pattern' to be a regex as
+      // documented in the hook_theme() API documentation. Removing it would
+      // break this feature.
       $matches = preg_grep('/^' . $pattern . '/', $patterns);
+
       if ($matches) {
-        foreach ($matches as $match) {
-          $file = substr($match, 0, strpos($match, '.'));
+        foreach ($matches as $file_uri => $template) {
           // Put the underscores back in for the hook name and register this
           // pattern.
           $arg_name = isset($info['variables']) ? 'variables' : 'render element';
-          $implementations[strtr($file, '-', '_')] = array(
-            'template' => $file,
-            'path' => dirname($files[$match]->uri),
+          $implementations[strtr($template, '-', '_')] = array(
+            'template' => $template,
+            'path' => dirname($file_uri),
             $arg_name => $info[$arg_name],
             'base hook' => $hook,
           );
diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
index f5ddfa9..457996d 100644
--- a/modules/simpletest/tests/theme.test
+++ b/modules/simpletest/tests/theme.test
@@ -646,3 +646,53 @@ class ThemeDebugMarkupTestCase extends DrupalWebTestCase {
   }
 
 }
+
+/**
+ * Tests the theme hook 'pattern' matching feature for template and function
+ * name overrides.
+ *
+ * As a side effect, because the test is rather tricky (we defined a pattern
+ * for which the prefix is different from the original template and function
+ * names) it also tests the fact we can use an array as the theme hook name
+ * when calling the theme() function.
+ */
+class ThemeHookPatternMatchingTestCase extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Theme hook pattern matching',
+      'description' => 'Tests that the \'pattern\' value in hook_theme() works as expected.',
+      'group' => 'Theme',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('theme_test');
+    theme_enable(array('test_theme', 'test_subtheme'));
+  }
+
+  /**
+   * Ensures that default template is found when 'pattern' is found.
+   */
+  function testDefaultAreFound() {
+    variable_set('theme_default', 'test_theme');
+    variable_set('admin_theme', 'test_theme');
+
+    $this->drupalGet('theme-test/theme-hook-pattern');
+    $this->assertText('Pattern matching original function');
+    $this->assertText('Pattern matching original template');
+  }
+
+  /**
+   * Ensures that pattern matching templates and functions are found.
+   */
+  function testOverridesWithPatternAreFound() {
+    variable_set('theme_default', 'test_subtheme');
+    variable_set('admin_theme', 'test_subtheme');
+
+    $this->drupalGet('theme-test/theme-hook-pattern');
+    $this->assertText('Function pattern matching override success');
+    $this->assertText('Template pattern matching override success');
+  }
+
+}
diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module
index 948d817..ec0cfe8 100644
--- a/modules/simpletest/tests/theme_test.module
+++ b/modules/simpletest/tests/theme_test.module
@@ -17,6 +17,16 @@ function theme_test_theme($existing, $type, $theme, $path) {
   $items['theme_test_foo'] = array(
     'variables' => array('foo' => NULL),
   );
+  $items['theme_test_pattern_template'] = array(
+    'template' => 'theme_test_pattern_template',
+    'pattern' => 'foo_(pattern|matching)(|_[0-9]+)',
+    'variables' => array(),
+  );
+  $items['theme_test_pattern_function'] = array(
+    'function' => 'theme_test_pattern_function',
+    'pattern' => 'foo_(pattern|matching)(|_[0-9]+)',
+    'variables' => array(),
+  );
   return $items;
 }
 
@@ -58,6 +68,12 @@ function theme_test_menu() {
     'access callback' => TRUE,
     'type' => MENU_CALLBACK,
   );
+  $items['theme-test/theme-hook-pattern'] = array(
+    'description' => "Serves a simple page with a template and a function that uses a theme hook pattern matching.",
+    'page callback' => '_theme_test_theme_hook_pattern',
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
   return $items;
 }
 
@@ -140,6 +156,34 @@ function _theme_test_drupal_add_region_content() {
 }
 
 /**
+ * Serves a simple page renderered using a Nyan Cat theme engine template.
+ */
+function _theme_test_engine_info_test() {
+  return array(
+    '#markup' => theme('theme_test_template_test'),
+  );
+}
+
+/**
+ * Serves a simple page with a template and a function that uses a theme hook
+ * pattern matching.
+ */
+function _theme_test_theme_hook_pattern() {
+  return array(
+    array(
+      '#theme' => array('foo_pattern', 'theme_test_pattern_function'),
+    ),
+    array(
+      '#theme' => array('foo_matching_123', 'theme_test_pattern_template'),
+    ),
+  );
+}
+
+function theme_test_pattern_function() {
+  return 'Pattern matching original function';
+}
+
+/**
  * Theme function for testing theme('theme_test_foo').
  */
 function theme_theme_test_foo($variables) {
diff --git a/modules/simpletest/tests/themes/test_subtheme/template.php b/modules/simpletest/tests/themes/test_subtheme/template.php
new file mode 100644
index 0000000..64642c9
--- /dev/null
+++ b/modules/simpletest/tests/themes/test_subtheme/template.php
@@ -0,0 +1,5 @@
+<?php
+
+function test_subtheme_foo_pattern() {
+  return "Function pattern matching override success.";
+}
diff --git a/modules/simpletest/tests/themes/test_subtheme/templates/foo-matching-123.tpl.php b/modules/simpletest/tests/themes/test_subtheme/templates/foo-matching-123.tpl.php
new file mode 100644
index 0000000..f8e92fc
--- /dev/null
+++ b/modules/simpletest/tests/themes/test_subtheme/templates/foo-matching-123.tpl.php
@@ -0,0 +1 @@
+Template pattern matching override success.
