diff options
Diffstat (limited to 'phpBB/phpbb')
22 files changed, 152 insertions, 41 deletions
diff --git a/phpBB/phpbb/cache/driver/file.php b/phpBB/phpbb/cache/driver/file.php index 497f00c06b..e9d2ba8e04 100644 --- a/phpBB/phpbb/cache/driver/file.php +++ b/phpBB/phpbb/cache/driver/file.php @@ -576,7 +576,7 @@ class file extends \phpbb\cache\driver\base if (function_exists('opcache_invalidate')) { - @opcache_invalidate($this->cache_file); + @opcache_invalidate($file); } try diff --git a/phpBB/phpbb/cache/driver/memcached.php b/phpBB/phpbb/cache/driver/memcached.php index 808e15afe8..7d66759ec2 100644 --- a/phpBB/phpbb/cache/driver/memcached.php +++ b/phpBB/phpbb/cache/driver/memcached.php @@ -65,7 +65,7 @@ class memcached extends \phpbb\cache\driver\memory $this->memcached->setOption(\Memcached::OPT_COMPRESSION, false); } - foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u) + foreach (explode(',', PHPBB_ACM_MEMCACHED) as $u) { preg_match('#(.*)/(\d+)#', $u, $parts); $this->memcached->addServer(trim($parts[1]), (int) trim($parts[2])); diff --git a/phpBB/phpbb/cache/driver/redis.php b/phpBB/phpbb/cache/driver/redis.php index eda774491c..eaeb529918 100644 --- a/phpBB/phpbb/cache/driver/redis.php +++ b/phpBB/phpbb/cache/driver/redis.php @@ -137,6 +137,10 @@ class redis extends \phpbb\cache\driver\memory */ function _write($var, $data, $ttl = 2592000) { + if ($ttl == 0) + { + return $this->redis->set($var, $data); + } return $this->redis->setex($var, $ttl, $data); } diff --git a/phpBB/phpbb/console/command/extension/enable.php b/phpBB/phpbb/console/command/extension/enable.php index a8312d5c15..f92de0069c 100644 --- a/phpBB/phpbb/console/command/extension/enable.php +++ b/phpBB/phpbb/console/command/extension/enable.php @@ -37,11 +37,18 @@ class enable extends command $io = new SymfonyStyle($input, $output); $name = $input->getArgument('extension-name'); + $extension = $this->manager->get_extension($name); + + if (!$extension->is_enableable()) + { + $io->error($this->user->lang('CLI_EXTENSION_NOT_ENABLEABLE', $name)); + return 1; + } if ($this->manager->is_enabled($name)) { $io->error($this->user->lang('CLI_EXTENSION_ENABLED', $name)); - return 2; + return 1; } $this->manager->enable($name); diff --git a/phpBB/phpbb/console/command/fixup/update_hashes.php b/phpBB/phpbb/console/command/fixup/update_hashes.php index 4bcc3b5d19..9a0e9bc798 100644 --- a/phpBB/phpbb/console/command/fixup/update_hashes.php +++ b/phpBB/phpbb/console/command/fixup/update_hashes.php @@ -101,9 +101,9 @@ class update_hashes extends \phpbb\console\command\command { $new_hash = $this->passwords_manager->hash($row['user_password'], array($this->default_type)); - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_password = "' . $this->db->sql_escape($new_hash) . '" - WHERE user_id = ' . (int) $row['user_id']; + $sql = 'UPDATE ' . USERS_TABLE . " + SET user_password = '" . $this->db->sql_escape($new_hash) . "' + WHERE user_id = " . (int) $row['user_id']; $this->db->sql_query($sql); $progress_bar->advance(); } diff --git a/phpBB/phpbb/cron/task/core/update_hashes.php b/phpBB/phpbb/cron/task/core/update_hashes.php index a4fe477d99..ba095abc8b 100644 --- a/phpBB/phpbb/cron/task/core/update_hashes.php +++ b/phpBB/phpbb/cron/task/core/update_hashes.php @@ -111,9 +111,9 @@ class update_hashes extends \phpbb\cron\task\base // Increase number so we know that users were selected from the database $affected_rows++; - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_password = "' . $this->db->sql_escape($new_hash) . '" - WHERE user_id = ' . (int) $row['user_id']; + $sql = 'UPDATE ' . USERS_TABLE . " + SET user_password = '" . $this->db->sql_escape($new_hash) . "' + WHERE user_id = " . (int) $row['user_id']; $this->db->sql_query($sql); } diff --git a/phpBB/phpbb/cron/task/text_reparser/reparser.php b/phpBB/phpbb/cron/task/text_reparser/reparser.php index 69392f5ac9..fa3bc67325 100644 --- a/phpBB/phpbb/cron/task/text_reparser/reparser.php +++ b/phpBB/phpbb/cron/task/text_reparser/reparser.php @@ -85,7 +85,7 @@ class reparser extends \phpbb\cron\task\base if ($this->resume_data === null) { - $this->reparser_manager->get_resume_data($this->reparser_name); + $this->resume_data = $this->reparser_manager->get_resume_data($this->reparser_name); } } @@ -96,7 +96,7 @@ class reparser extends \phpbb\cron\task\base { if ($this->resume_data === null) { - $this->reparser_manager->get_resume_data($this->reparser_name); + $this->resume_data = $this->reparser_manager->get_resume_data($this->reparser_name); } if (!isset($this->resume_data['range-max']) || $this->resume_data['range-max'] >= $this->resume_data['range-min']) diff --git a/phpBB/phpbb/db/migration/data/v320/text_reparser.php b/phpBB/phpbb/db/migration/data/v320/text_reparser.php index 03c5d39fe4..6b8cf93cc9 100644 --- a/phpBB/phpbb/db/migration/data/v320/text_reparser.php +++ b/phpBB/phpbb/db/migration/data/v320/text_reparser.php @@ -54,13 +54,16 @@ class text_reparser extends \phpbb\db\migration\container_aware_migration /** @var manager $reparser_manager */ $reparser_manager = $this->container->get('text_reparser.manager'); - /** @var reparser_interface[] $reparsers */ - $reparsers = $this->container->get('text_reparser_collection'); - - // Initialize all reparsers - foreach ($reparsers as $name => $reparser) + if (!is_array($resume_data)) { - $reparser_manager->update_resume_data($name, 1, $reparser->get_max_id(), 100); + /** @var reparser_interface[] $reparsers */ + $reparsers = $this->container->get('text_reparser_collection'); + + // Initialize all reparsers + foreach ($reparsers as $name => $reparser) + { + $reparser_manager->update_resume_data($name, 1, $reparser->get_max_id(), 100); + } } // Sometimes a cron job is too much diff --git a/phpBB/phpbb/db/migrator.php b/phpBB/phpbb/db/migrator.php index d7d7f18d2b..6c026c3ae1 100644 --- a/phpBB/phpbb/db/migrator.php +++ b/phpBB/phpbb/db/migrator.php @@ -503,11 +503,14 @@ class migrator return; } - foreach ($this->migration_state as $name => $state) + foreach ($this->migrations as $name) { - if (!empty($state['migration_depends_on']) && in_array($migration, $state['migration_depends_on'])) + $state = $this->migration_state($name); + + if ($state && in_array($migration, $state['migration_depends_on']) && ($state['migration_schema_done'] || $state['migration_data_done'])) { $this->revert_do($name); + return; } } diff --git a/phpBB/phpbb/db/tools/mssql.php b/phpBB/phpbb/db/tools/mssql.php index d31aa2ba0b..23b49aab44 100644 --- a/phpBB/phpbb/db/tools/mssql.php +++ b/phpBB/phpbb/db/tools/mssql.php @@ -524,7 +524,7 @@ class mssql extends tools { $statements = array(); - if ($this->is_sql_server_2000()) + if ($this->mssql_is_sql_server_2000()) { $this->check_index_name_length($table_name, $index_name); } @@ -541,7 +541,7 @@ class mssql extends tools { $statements = array(); - if ($this->is_sql_server_2000()) + if ($this->mssql_is_sql_server_2000()) { $this->check_index_name_length($table_name, $index_name); } diff --git a/phpBB/phpbb/event/kernel_exception_subscriber.php b/phpBB/phpbb/event/kernel_exception_subscriber.php index b7a54f2608..373e59b0c8 100644 --- a/phpBB/phpbb/event/kernel_exception_subscriber.php +++ b/phpBB/phpbb/event/kernel_exception_subscriber.php @@ -16,6 +16,7 @@ namespace phpbb\event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpFoundation\Response; @@ -23,6 +24,13 @@ use Symfony\Component\HttpFoundation\Response; class kernel_exception_subscriber implements EventSubscriberInterface { /** + * Set to true to show full exception messages + * + * @var bool + */ + protected $debug; + + /** * Template object * * @var \phpbb\template\template @@ -44,9 +52,11 @@ class kernel_exception_subscriber implements EventSubscriberInterface * * @param \phpbb\template\template $template Template object * @param \phpbb\language\language $language Language object + * @param bool $debug Set to true to show full exception messages */ - public function __construct(\phpbb\template\template $template, \phpbb\language\language $language) + public function __construct(\phpbb\template\template $template, \phpbb\language\language $language, $debug = false) { + $this->debug = $debug || defined('DEBUG'); $this->template = $template; $this->language = $language; $this->type_caster = new \phpbb\request\type_cast_helper(); @@ -69,6 +79,10 @@ class kernel_exception_subscriber implements EventSubscriberInterface { $message = $this->language->lang_array($message, $exception->get_parameters()); } + else if (!$this->debug && $exception instanceof NotFoundHttpException) + { + $message = $this->language->lang('PAGE_NOT_FOUND'); + } // Show <strong> text in bold $message = preg_replace('#<(/?strong)>#i', '<$1>', $message); @@ -99,7 +113,7 @@ class kernel_exception_subscriber implements EventSubscriberInterface $data['message'] = $message; } - if (defined('DEBUG')) + if ($this->debug) { $data['trace'] = $exception->getTrace(); } diff --git a/phpBB/phpbb/feed/topics_active.php b/phpBB/phpbb/feed/topics_active.php index 7ae0bde56b..ea9ee97b9d 100644 --- a/phpBB/phpbb/feed/topics_active.php +++ b/phpBB/phpbb/feed/topics_active.php @@ -119,7 +119,7 @@ class topics_active extends topic_base FROM ' . FORUMS_TABLE . ' WHERE forum_type = ' . FORUM_POST . ' AND ' . $this->db->sql_bit_and('forum_options', FORUM_OPTION_FEED_EXCLUDE, '= 0') . ' - AND ' . $this->db->sql_bit_and('forum_flags', log(FORUM_FLAG_ACTIVE_TOPICS, 2), '<> 0'); + AND ' . $this->db->sql_bit_and('forum_flags', round(log(FORUM_FLAG_ACTIVE_TOPICS, 2)), '<> 0'); $result = $this->db->sql_query($sql); $forum_ids = array(); diff --git a/phpBB/phpbb/install/module/install_finish/task/install_extensions.php b/phpBB/phpbb/install/module/install_finish/task/install_extensions.php index 553a30ea28..eee13a6581 100644 --- a/phpBB/phpbb/install/module/install_finish/task/install_extensions.php +++ b/phpBB/phpbb/install/module/install_finish/task/install_extensions.php @@ -118,6 +118,14 @@ class install_extensions extends \phpbb\install\task_base try { + $extension = $this->extension_manager->get_extension($ext_name); + + if (!$extension->is_enableable()) + { + $this->iohandler->add_log_message(array('CLI_EXTENSION_NOT_ENABLEABLE', $ext_name)); + continue; + } + $this->extension_manager->enable($ext_name); $extensions = $this->get_extensions(); diff --git a/phpBB/phpbb/log/log.php b/phpBB/phpbb/log/log.php index cf3db365a4..5aad7ee326 100644 --- a/phpBB/phpbb/log/log.php +++ b/phpBB/phpbb/log/log.php @@ -747,7 +747,7 @@ class log implements \phpbb\log\log_interface foreach ($log as $key => $row) { $log[$key]['viewtopic'] = (isset($topic_auth['f_read'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&t=' . $row['topic_id']) : false; - $log[$key]['viewpost'] = (isset($topic_auth['f_read'][$row['topic_id']]) && $row['post_id']) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&t=' . $row['topic_id'] . '&p=' . $row['post_id']) : false; + $log[$key]['viewpost'] = (isset($topic_auth['f_read'][$row['topic_id']]) && $row['post_id']) ? append_sid("{$this->phpbb_root_path}viewtopic.{$this->php_ext}", 'f=' . $topic_auth['f_read'][$row['topic_id']] . '&t=' . $row['topic_id'] . '&p=' . $row['post_id'] . '#p' . $row['post_id']) : false; $log[$key]['viewlogs'] = (isset($topic_auth['m_'][$row['topic_id']])) ? append_sid("{$this->phpbb_root_path}mcp.{$this->php_ext}", 'i=logs&mode=topic_logs&t=' . $row['topic_id'], true, $this->user->session_id) : false; } } diff --git a/phpBB/phpbb/notification/manager.php b/phpBB/phpbb/notification/manager.php index 6923d96392..1cd7e5bc9a 100644 --- a/phpBB/phpbb/notification/manager.php +++ b/phpBB/phpbb/notification/manager.php @@ -475,9 +475,10 @@ class manager if ($type instanceof \phpbb\notification\type\type_interface && $type->is_available()) { $options = array_merge(array( - 'id' => $type->get_type(), - 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), - 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS', + 'type' => $type, + 'id' => $type->get_type(), + 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), + 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS', ), (($type::$notification_option !== false) ? $type::$notification_option : array())); $this->subscription_types[$options['group']][$options['id']] = $options; @@ -509,6 +510,7 @@ class manager foreach ($this->get_available_subscription_methods() as $method_name => $method) { $subscription_methods[$method_name] = array( + 'method' => $method, 'id' => $method->get_type(), 'lang' => str_replace('.', '_', strtoupper($method->get_type())), ); diff --git a/phpBB/phpbb/notification/method/email.php b/phpBB/phpbb/notification/method/email.php index 21a6559012..56dd1e9367 100644 --- a/phpBB/phpbb/notification/method/email.php +++ b/phpBB/phpbb/notification/method/email.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Email notification method class * This class handles sending emails for notifications @@ -56,10 +58,14 @@ class email extends \phpbb\notification\method\messenger_base /** * Is this method available for the user? * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. If provided, this + * method additionally checks if the type provides an email template. + * @return bool */ - public function is_available() + public function is_available(type_interface $notification_type = null) { - return $this->config['email_enable'] && $this->user->data['user_email']; + return parent::is_available($notification_type) && $this->config['email_enable'] && $this->user->data['user_email']; } /** diff --git a/phpBB/phpbb/notification/method/jabber.php b/phpBB/phpbb/notification/method/jabber.php index 509c6b432c..81fdb378e2 100644 --- a/phpBB/phpbb/notification/method/jabber.php +++ b/phpBB/phpbb/notification/method/jabber.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Jabber notification method class * This class handles sending Jabber messages for notifications @@ -56,10 +58,14 @@ class jabber extends \phpbb\notification\method\messenger_base /** * Is this method available for the user? * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. If provided, this + * method additionally checks if the type provides an email template. + * @return bool */ - public function is_available() + public function is_available(type_interface $notification_type = null) { - return ($this->global_available() && $this->user->data['user_jabber']); + return parent::is_available($notification_type) && $this->global_available() && $this->user->data['user_jabber']; } /** diff --git a/phpBB/phpbb/notification/method/messenger_base.php b/phpBB/phpbb/notification/method/messenger_base.php index 1f5525accc..32e79aa936 100644 --- a/phpBB/phpbb/notification/method/messenger_base.php +++ b/phpBB/phpbb/notification/method/messenger_base.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Abstract notification method handling email and jabber notifications * using the phpBB messenger. @@ -43,6 +45,19 @@ abstract class messenger_base extends \phpbb\notification\method\base } /** + * Is this method available for the user? + * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. This method returns false + * only if the type is provided and if it doesn't provide an email template. + * @return bool + */ + public function is_available(type_interface $notification_type = null) + { + return $notification_type === null || $notification_type->get_email_template() !== false; + } + + /** * Notify using phpBB messenger * * @param int $notify_method Notify method for messenger (e.g. NOTIFY_IM) diff --git a/phpBB/phpbb/path_helper.php b/phpBB/phpbb/path_helper.php index 154361ef64..5b6db35f23 100644 --- a/phpBB/phpbb/path_helper.php +++ b/phpBB/phpbb/path_helper.php @@ -496,4 +496,17 @@ class path_helper return $page; } + + /** + * Tells if the router is currently in use (if the current page is a route or not) + * + * @return bool + */ + public function is_router_used() + { + // Script name URI (e.g. phpBB/app.php) + $script_name = $this->symfony_request->getScriptName(); + + return basename($script_name) === 'app.' . $this->php_ext; + } } diff --git a/phpBB/phpbb/search/fulltext_sphinx.php b/phpBB/phpbb/search/fulltext_sphinx.php index 89c615e087..59c3d55076 100644 --- a/phpBB/phpbb/search/fulltext_sphinx.php +++ b/phpBB/phpbb/search/fulltext_sphinx.php @@ -648,7 +648,7 @@ class fulltext_sphinx $this->sphinx->SetFilter('deleted', array(0)); $this->sphinx->SetLimits($start, (int) $per_page, SPHINX_MAX_MATCHES); - $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + $result = $this->sphinx->Query($search_query_prefix . $this->sphinx->EscapeString(str_replace('"', '"', $this->search_query)), $this->indexes); // Could be connection to localhost:9312 failed (errno=111, // msg=Connection refused) during rotate, retry if so @@ -656,7 +656,7 @@ class fulltext_sphinx while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--) { usleep(SPHINX_CONNECT_WAIT_TIME); - $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + $result = $this->sphinx->Query($search_query_prefix . $this->sphinx->EscapeString(str_replace('"', '"', $this->search_query)), $this->indexes); } if ($this->sphinx->GetLastError()) @@ -679,7 +679,7 @@ class fulltext_sphinx $start = floor(($result_count - 1) / $per_page) * $per_page; $this->sphinx->SetLimits((int) $start, (int) $per_page, SPHINX_MAX_MATCHES); - $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + $result = $this->sphinx->Query($search_query_prefix . $this->sphinx->EscapeString(str_replace('"', '"', $this->search_query)), $this->indexes); // Could be connection to localhost:9312 failed (errno=111, // msg=Connection refused) during rotate, retry if so @@ -687,7 +687,7 @@ class fulltext_sphinx while (!$result && (strpos($this->sphinx->GetLastError(), "errno=111,") !== false) && $retries--) { usleep(SPHINX_CONNECT_WAIT_TIME); - $result = $this->sphinx->Query($search_query_prefix . str_replace('"', '"', $this->search_query), $this->indexes); + $result = $this->sphinx->Query($search_query_prefix . $this->sphinx->EscapeString(str_replace('"', '"', $this->search_query)), $this->indexes); } } diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php index c5b50c2b07..6b5b8f2625 100644 --- a/phpBB/phpbb/session.php +++ b/phpBB/phpbb/session.php @@ -91,9 +91,18 @@ class session $page_name .= str_replace('%2F', '/', urlencode($symfony_request_path)); } - // current directory within the phpBB root (for example: adm) - $root_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath($root_path))); - $page_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath('./'))); + if (substr($root_path, 0, 2) === './' && strpos($root_path, '..') === false) + { + $root_dirs = explode('/', str_replace('\\', '/', rtrim($root_path, '/'))); + $page_dirs = explode('/', str_replace('\\', '/', '.')); + } + else + { + // current directory within the phpBB root (for example: adm) + $root_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath($root_path))); + $page_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath('./'))); + } + $intersection = array_intersect_assoc($root_dirs, $page_dirs); $root_dirs = array_diff_assoc($root_dirs, $intersection); diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index 7719ce5afa..d5ad8283d9 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -273,6 +273,11 @@ class factory implements \phpbb\textformatter\cache_interface { $configurator->BBCodes->addCustom($bbcode['usage'], $bbcode['template']); } + if (isset($configurator->tags['QUOTE'])) + { + // Remove the nesting limit and let other services remove quotes at parsing time + $configurator->tags['QUOTE']->nestingLimit = PHP_INT_MAX; + } // Modify the template to disable images/flash depending on user's settings foreach (array('FLASH', 'IMG') as $name) @@ -323,6 +328,9 @@ class factory implements \phpbb\textformatter\cache_interface // Only parse emoticons at the beginning of the text or if they're preceded by any // one of: a new line, a space, a dot, or a right square bracket $configurator->Emoticons->notAfter = '[^\\n .\\]]'; + + // Ignore emoticons that are immediately followed by a "word" character + $configurator->Emoticons->notBefore = '\\w'; } // Load the censored words @@ -382,7 +390,18 @@ class factory implements \phpbb\textformatter\cache_interface unset($configurator->tags['censor:tag']); } - $objects = $configurator->finalize(); + $objects = $configurator->finalize(); + + /** + * Access the objects returned by finalize() before they are saved to cache + * + * @event core.text_formatter_s9e_configure_finalize + * @var array objects Array containing a "parser" object, a "renderer" object and optionally a "js" string + * @since 3.2.2-RC1 + */ + $vars = array('objects'); + extract($this->dispatcher->trigger_event('core.text_formatter_s9e_configure_finalize', compact($vars))); + $parser = $objects['parser']; $renderer = $objects['renderer']; @@ -518,7 +537,9 @@ class factory implements \phpbb\textformatter\cache_interface protected function extract_templates($template) { // Capture the template fragments - preg_match_all('#<!-- BEGIN (.*?) -->(.*?)<!-- END .*? -->#s', $template, $matches, PREG_SET_ORDER); + // Allow either phpBB template or the Twig syntax + preg_match_all('#<!-- BEGIN (.*?) -->(.*?)<!-- END .*? -->#s', $template, $matches, PREG_SET_ORDER) ?: + preg_match_all('#{% for (.*?) in .*? %}(.*?){% endfor %}#s', $template, $matches, PREG_SET_ORDER); $fragments = array(); foreach ($matches as $match) |
